Persist sidebar with fetched data through all the routes - javascript

Overview:
I got this route in my react application
example.com/routeName/:id
This is the entrypoint for my users, they click on the link and see a screen with informations provided by this hash in a right sidebar component.
example.com/routeName/94a31bb9-e6ae-4e9a-8749-0807f9efd0001
Below I attached a kind of wireframe, trying to be more clear about the sidebar:
Wireframe to understand how my app components are shown at the end, and why the hash is important in a visual way
This works fine, I can get the hash and make the request inside my dumb component to display data.
The problem:
I'm doing the fetch to the api in my Layout component, and passing down the data via props to my sidebar that is fixed in my layout, because it is displayed through all the user experience
When my user sign in, my route change, from:
example.com/routeName/94a31bb9-e6ae-4e9a-8749-0807f9efd0001
to:
example.com/nextRouteNameHere
I still see the component with the information I need, but if I refresh the page at the second route, the app crashes, because my sidebar cant find the hash to fetch my api and get the necessary data.
What I've been trying to do:
Persist this route with the hash and make the route change internally - but that doesn't makes sense to me at all, and I could not find any resources even close to this strategy.
Persist this hash in localstorage and get the hash with hooks inside the sidebar. This way if I can't find my hash at the url it'll be in the localstorage and I can fetch my api to get the data. I did it, but doesn't work because the components presents the some behavior, trying to render with no data.
As I'm using redux to manage the global state I've already taken the hash and the fetch response to the localstorage, this way I can take this data through all the aplication, but the hash is what makes my hooks update when I tried to wrote this strategy, and when the user updates the page I got the same behavior, because the hook could not find the :id on the url.
Final question
How can I persist the sidebar with the fetched data I did when I load the page?
NOTE
I did not post the code because at this moment I got too much code trying to solve this and I'm stuck in this problem for almost one month. I'll update this post later on.

Related

How to keep data in react after browser back

I have a React app(Gatsby) and using #reach/router.
There are multiple pages like user page, follower page...etc.
Fetching data in useEffect hook and here is the problem.
When I navigate to another page from the current page, I'm passing a param, sending it to the next component, for example to a user page, then calling API in useEffect hook in the component with the param. Then you can go to another component like follower page with a param, calling api in useEffect again.
When I try to go back with browser back, the previous data updates again. Ideally I don't want it to get updated every time when I go back to the previous page.
I understand that this happens because I'm calling it in the useEffect hook. Thought about passing data as props with navigate (ex. navigate("/follower", state: { data })), but I think navigate to the page first and fetching data is the best solution for users because when fetching data is taking some time, I want to show skeleton or a loading indicator in the next page. The ideal experience is something like twitter.
Not sure this isn't working because I'm using #reach/router instead of react-router-dom
Any advise is appreciated.
EDIT:
Apologise for my poor explanation.
My problem here is when I keep going back to previous pages like this user page → follower → (another)user → follower ... , browser doesn't remember what were there and useEffect fires everytime. It wasn't happening with react-router before?
My guess is that since I'm using GatsbyJS(SSR) and client-side routing, somehow it generates pages every time even the browser back triggered instead painting it again? I'm not familiar with SSR w/ cliet-routing, hoping to get some info somewhere..
The only way it comes to my mind is using local storage or cookies, within a useEffect with deps ([]), otherwise, each rendering of the page (moving back and forward for example) will trigger again all side-effects available.
Alternatively, you can use useEffect's deps, to only fire the useEffect if the needed dependency has changed.
const SomePage=(props)=>{
const isLoggedIn=props.isLoggedIn || localStorage.getItem('logIn')
useEffect(()=>{
// this will only be fired when yourDeps changes
}, [yourDeps])
return {isLoggedIn ? <div>Hello ${user}</div> : <Link to="/log-in">Log in</Link>
}
You deps (yourDeps) can also check values that you are sending through the location state or from local storate/cookies.
You can also use useRef hook to store "old" values from references but given your generic explanation, I'm not sure which case fits you better.

Can I pull out URL parameters from different format than normally used on react-router-dom?

We're about to relaunch an existing website, that was previously a wordpress site and now is going to be a react site.
The routing is done via react-router-dom and there are a couple of pages which will render dynamically depending on the parameters in the url.
There is one page that is basically used for a user to put in their data and sign up. This component has two parameters, one which is the user-id and another one.
The link to that component gets sent out to people who have previously expressed interest through the website and then gets mailed to them. This component also existed on the wordpress site, but lived on a very different route.
So now when we relaunch we want to make sure people with the old link still get redirected to the new, correct component instead of getting a 404.
My question is now, is there a way for me to redirect from the old url (which also contains both parameters in the slug) to the new one while passing on the parameters?
The old url looked like this:
.com/confirmation-FIRST_PARAM/?IDref=:SECOND_PARAM
The new url for rendering this page looks like this:
.com/confirmation/:FIRST_PARAM/:SECOND_PARAM
My first naive approach was to just create a new route in my App.js file like so:
<Route
path="/confirmation-:firstParam/?IDref=:id"
component={Confirmation}
/>
which doesn't work. I guess that's because it's not really complying with how parameters are passed in react-router. Is there a way to redirect users to the appropriate new page?
I now managed to get it to work, but I don't think it's the smartest way possible (or at least it feels like there should be a leaner solution).
I added another route for the old links and then within the component I am using the useLocation() hook to figure out at which pathname the component actually gets rendered.
Depending on that I set the first parameter. The user id is then extracted by using the search property of the location object:
oldId = location.search.split("=")[1];
Using the old id, the other param (pkf) and the useHistory hook I then redirect to the new route:
history.push(`/confirmation/${oldId}/${oldPkf}`);
Whenever now a user accesses the component through an old link, the parameters get pulled from the location and the user gets redirected to the new link.

How to keep props data persisted in Vue?

I have an application that is simplified into 4 Vue components:
TextbookSelection
TextbookView
ChapterView
Textbook Header
In my TextbookSelection component, I make an API call to retrieve all my available textbooks from my database. I then populate the front end with the data and dynamically create router links that will take the user to the next vue component, TextbookView if they click on one of them. I pass in a textbook data object as a prop that contains info about this textbook so that the TextbookView component can use it to populate instead of calling for the textbook data again.
In my TextbookSelection.vue, these router-links are created.
<router-link
:to="{
name: 'TextbookView',
params: { textbook: textbook},
}"
In my TextbookView, I have props: ["textbook"] written. I also have a TextbookHeader component in there that displays all the info about the selected textbook, so I pass in the textbook data in as a prop to the TextbookHeadercomponent as well. I have this as a separate vue component because I want the header to persist as the user goes further into the textbook, such as the ChapterView component. So as you can see, I am taking the same textbook data object I received from my first component (TextbookSelection) and passing it into every component if the user goes deeper into the textbook.
This works on the first pass; the user can select a textbook and it displays all the correct textbook information in the header. But then problem arises when the user hits "back" in their browser, or if they refresh the page. Suddenly, the header disappears because the prop data (Textbook data object) is now missing.
Error in render: "TypeError: Cannot read property 'id' of undefined"
found in
---> <TextbookHeaderView> at src/components/TextbookHeader.vue
<ChapterView> at src/components/ChapterView.vue
<App> at src/App.vue
How should I approach this problem? I don't know if what I'm doing is the correct way or not, but basically what I'd like is:
Have a header vue component
Have textbook data be persisted (user can go back and forth/refresh and the header will still have correct info)
Not have to constantly pull in data whenever the header component is created
I would recommend you use VueX for state management.
You could have an action that fetches your data from the database upon the creation of your home component and then update the store according to the fetched data.
This allows all your components to have access to your data that is persisted through routing.
Your TextbookHeader and all other components then have a single source of truth (the Store) and you don't have to re-fetch data (which you shouldn't anyway).
For more information on Vuex, please visit the official documentation

javascript go back and get previous page FINAL STATE

I am building a page that retrieves some Github repositories and then displays a punchcard chart with the data returned by the API (sorry for the screenshots in advance, but I was having trouble with the in-built code styling). I start with a semi-empty html page and I build stuff as I get the info from the API calls.
Firstly, the user logs in to get all their repos, but I don't store information about it, I just pass their credentials in the header (app.js)
Then, with the response I paint a list of all the repos (app.js).
When clicked in one repo, a new html is load, and has js that calls the Github API to retrieve the punchcard and paint the charts (repo.js).
What I need to accomplish is that, if while being in that second html I click on the goBack button, I go back to the previous page, but having the repo list already painted there. Is there any way I can do it without having to log in (do it all over) again?
The app is deployed here, and all the code can be found here.
Thanks for the help.

Populating an ngrx store with data shared between routes

I have a multi-page form spanning over several routes. All of the routes need the same data shared with them from an API. I can store the response of the API inside ngrx/store and trigger the API call using an effect. My question is more about where to initiate the API call. The API call needs to be made once the user is authenticated, which happens on the very first route I hit (before the first part of the multi page form is visited). The two options I've come up with are:
Triggering the effect inside each route's component meaning I'll just have to request the information every time I visit a route. A guard will prevent all pages being accessible while the user isn't authenticated.
Listen to the authenticated success action inside an effect and make the request to the API there.
I'm sure both are perfectly acceptable and have their trade offs. It'd just be good to get a few opinions!
The second would be the best, requesting the information on demand ergo when the user is authentified and its allowed to use it makes more sense. Inside of the guard, as u said, you should dispatch the action to load the information before you return true/of(true) to signalize that the route can be activated. The naive approach for this would trigger an information request everythime that you try to activate the guarded route.

Categories

Resources