Issue
Currently I have a React component as follows:
export default function Company() {
const { path } = useRouteMatch();
const [lookupData, setLookupData] = useState();
const queryParams = new URLSearchParams(window.location.search);
const ticker = queryParams.get("ticker");
useEffect(() => {
const lookupPromise = getLookupData(ticker);
lookupPromise.then((data) => {
setLookupData(data);
});
}, []);
return lookupData ? (
<>
<h2>{lookupData.data.title}</h2>
<ul class="leftNav">
<li class="mainList">
<a href="/Company/Overview">Overview</a>
</li>
<li class="mainList">Financials</li>
<li class="indentList">
<a href="/Company/KeyFinancials">Key Financials</a>
</li>
<li class="indentList">
<a href="/Company/IncomeStatement">Income Statement</a>
</li>
</ul>
<div class="body">
<BrowserRouter>
<Switch>
<Route path={`${path}/Overview`}>
<Overview cik={lookupData.data.cik} />
</Route>
<Route path={`${path}/KeyFinancials`}>
<KeyFinancials cik={lookupData.data.cik} />
</Route>
<Route path={`${path}/IncomeStatement`}>
<IncomeStatement cik={lookupData.data.cik} />
</Route>
<Route exact path={`${path}/`}>
<Redirect to={`${path}/Overview`} />
</Route>
</Switch>
</BrowserRouter>
</div>
</>
) : "waiting for lookup data";
}
In each of the components, I use the cik to get a bunch more data. Right now it loads the overview in (using the cik) correctly (since that's the default component). However, if I switch components or refresh the page, the cik goes into the component as null (which breaks a bunch of stuff). Does anyone know how to fix this and make the parameter persist?
Solution
Issue
The code is rendering raw anchor <a>
tags to navigate between pages, which when clicked make a page request to the server and reloads the page, i.e. the entire app is remounted. React state only exists in memory, so upon a page reload it goes away unless you are persisting and re-initializing it from localStorage.
Solution
- Use the
Link
component fromreact-router-dom
to link between pages so the page/app isn't reloaded/remounted. - Lift the
BrowserRouter
higher in the ReactTree so that theLink
component is rendered within a routing context. - Use the
useLocation
hook to read thesearch
string.
Example:
import {
Route,
Switch,
Link,
useLocation,
useRouteMatch,
} from 'react-router-dom';
export default function Company() {
const { path, url } = useRouteMatch();
const { search } = useLocation();
const [lookupData, setLookupData] = useState();
const searchParams = new URLSearchParams(search);
const ticker = searchParams.get("ticker");
useEffect(() => {
getLookupData(ticker)
.then(({ data }) => { // <-- destructure `data` property
setLookupData(data);
});
}, [ticker]); // <-- add missing dependency
return lookupData ? (
<>
<h2>{lookupData.title}</h2>
<ul class="leftNav">
<li class="mainList">
<Link to={`${url}/Overview`}>Overview</Link>
</li>
<li class="mainList">Financials</li>
<li class="indentList">
<Link to={`${url}/KeyFinancials`}>Key Financials</Link>
</li>
<li class="indentList">
<Link to={`${url}/IncomeStatement`}>Income Statement</Link>
</li>
</ul>
<div class="body">
<Switch>
<Route path={`${path}/Overview`}>
<Overview cik={lookupData.cik} />
</Route>
<Route path={`${path}/KeyFinancials`}>
<KeyFinancials cik={lookupData.cik} />
</Route>
<Route path={`${path}/IncomeStatement`}>
<IncomeStatement cik={lookupData.cik} />
</Route>
<Redirect exact from={path} to={`${path}/Overview`} />
</Switch>
</div>
</>
) : "waiting for lookup data";
}
<BrowserRouter>
...
<Company />
...
</BrowserRouter>
Answered By - Drew Reese
Answer Checked By - - Pedro (ReactFix Volunteer)