How to not lose my information in props when i reaload my pages? - reactjs

I'm new in react and i'm studing a method to make connections with Firebase to my Project.
With props I can pass an ID and search it in firebase, but when i reload my page, all the props are lost and i have this message in the image:
The value is Undefined because the props are losing their values
Is there any other way for this information don't lost?
I dont try anything because idk what to do.
The ID in the props will be used in useEffect() on my page.
Edit:
There is the Cards Image:
Cards
And here it's when i click on the card "Pinscher":
The page when i click on Pinscher
But when i Reload the page "Pinscher", i lost the Id passed with props.

The most straightforward way I know to persist data over a reload is to save it to localstorage or sessionstorage. Which one to use--and whether it's appropriate--depend on your actual use case, which we can't comment on without more knowledge of your project.
Assuming you did want to use that, a basic implementation would be to write helper functions when you set state to also save the data to storage. Your initial state load would then look for storage information as its default and then fallback to null if it can't find anything in storage.
const [arbData, setArbData] = useState(window.localStorage.getItem('arbData'));
const setArbDataWrapper = (data) => {
setArbData(data);
window.localStorage.set('arbData', data);
}

Related

How to save selected options while going back ReactJS?

I have 2 useEffect hooks for fetching all cars, then user can select some options, and component will rerendered with new fetched data, considering selected options, further user can navigate to some component and after that go back and all data will be fetched with initial state (all selected options will be erased). How to save selected options and use it for next renderings ? Furthermore It would be better to use only one hook, but i was confused in logic.
useEffect(() => {
fetchManufacturers().then(data => car.setManufacturers(data))
fetchCarNames().then(data => car.setCarNames(data))
fetchCars( null, null, 1, car.limit).then(data => {
car.setCars(data.rows)
car.setTotalCount(data.count)
})
},[car, car.page, car.selectedManufacturer, car.selectedCarName])
useEffect(() => {
fetchCars(car.selectedManufacturer.id , car.selectedCarName.id, car.page, car.limit).then(data => {
car.setCars(data.rows)
car.setTotalCount(data.count)
})
}, [car.page, car.selectedManufacturer, car.selectedCarName])
I tried to use getters and setters, but it save the 1 step back selected option.
You could either store your data in a parent component, but I suggest storing it in a context. Since it is related to a user decision, it could be interesting to get these data from wherever you want.
You can find a simple example on how to use a the React contextApi in my answer to another post.
There is other state management, but I like this one for its simplicity.
React Redux is great for what you're trying to do. You can fetch all the raw car data once and save it in Redux. Every component can then fetch that data as needed. You can even store your user's car selections in Redux if you need to share that state between components.

Implementing a local like/unlike feature in NextJS and making it persist between client-side routes

I have a blog front-end app built using NextJS and it looks like this:
Each card here is a functional component called PostPreview.jsx. As you can see, each component comes with a heart icon. By default, this icon is to stay gray. However, when clicked it turns red, signifying that the post has been liked. This action only occurs if the user is logged in. If not, clicking the heart icon presents a login modal.
Right now, I'm only focussing on making the "like" persist between client-side navigations, i.e. without any interaction with the db/server.
As of now, clicking the icon, toggles the color alright. However, it fails to persist when you navigate away, say, by clicking on a post title and then hitting the back button to return to this page. What is the recommended way to achieve this functionality?
The entire codebase can be found at my repo here: https://github.com/amitschandillia/proost/tree/master/web
The code for the component in question (PostPreview.jsx) is at: https://github.com/amitschandillia/proost/blob/master/web/components/blog/PostPreview.jsx
The site is live at https://www.schandillia.com/blog.
I understand I could use Redux, but not sure how to prevent the value from being reset upon each re-render even when using Redux.
Illustrating the problem better
Visit blog page; several instances of component (PostPreview) render for the first time:
Receive array of post "likers" via the likedBy prop object.
Retrieve logged-in user's ID from the Redux store via userInfo.userID.
Look up userInfo.userID against the likedBy.readers array of IDs.
If user ID exists in readers array, post is liked, set liked to error to turn the heart icon red and push post's id to the likedPosts redux store.
If user ID doesn't exist in readers array, leave liked to inherit to leave it gray and remove post's id from the likedPostsredux store.
Like a post; click the heart icon:
Set liked to error to turn the heart icon red.
Push post's id to the likedPostsredux store.
Unlike a post; click the heart icon:
Set liked to inherit to turn the heart icon red.
Remove post's id from the likedPostsredux store.
Now click any link on page to navigate away from the page (client-side routing, no server contact here). Then hit the browser's back button to return to the blog page.
At this point, the component (PostPreview) re-renders and the redux store will be reset in accordance with the original likedBy prop object. This, of course, means that all the changes since the first render are gone. This is where I need help. How would you handle such a situation where user interactions like likes and dislikes have to be persisted across client-side navigations and also honored across re-renders?
I see two ways:
1) Simple: By using local storage you can write array of likes
likes: [likedPostId1, likedPostId2, ...]
And then in PostPreview check if current card id included in likes array
let isLiked = likes.includes(currentPostId);
2) Right: By using Redux, it's the same way, but you'll store likes array in Redux and use for page navigation react-router.
It's essential to understand how navigation works in SPA
For simple SPA implementation:
Simple solution would be to just use Context API with hooks (refresh will reset it)
1a. Another simple solution is to use LocalStorage with hooks (refresh or back button will persist it)
Than you could refactor it into REDUX (a lot of boilerplate)
2a. so maybe you could just use GraphQL as an app state
Here is an article explaining how to do that highlighting Redux vs hooks with Context API:
https://www.sitepoint.com/replace-redux-react-hooks-context-api/
Also it's worth to note that REDUX or GraphQL will not persist it itself between refreshes
4. If user ID exists in readers array, post is liked, set liked to error to turn the heart icon red and push post's id to the likedPosts redux store.
5. If user ID doesn't exist in readers array, leave liked to inherit to leave it gray and remove post's id from the likedPosts redux store.
You should wrap #4. and #5. as one action in redux store.
const mapDispatchToProps = (dispatch) => {
return {
onUpdateLikedPosts: (data) => dispatch(actions.updateLikedPostsAction(data))
};
};
And, Execture the function at when component of PostPreview is mounted.
const componentDidMount() {
this.props.onUpdateLikedPosts(data)
}

Reactjs Refresh page

When I press the F5 button on my browser to refresh my page, I lose all the values that I once had in my state. How to keep the values updated in my State by pressing the F5 key
It is a bit tricky, but not impossible.
First, do this: Check if page gets reloaded or refreshed in Javascript
You need to recognize that your site has been reloaded and previous code might help you with that.
Next is, to pick place, where to store your state, before reload actually occurs. One option is to store it in local storage.
It is best, for you, if all data which you are trying to preserve, are on server already. If not, then you need some "middle point" between refreshes.
You could do something like this on the part where you recognize that store is reloading:
// Site is reloading
// Store information into users local storage... all current
// states with information of url under which data is stored
On the place, when site is loaded again, you can do:
// Check if there is something stored in local storage for this specific site
// When yes, fill in the states again with configured data
Now, this can make you a lot of trouble, but it would do the work. For example, what about situations when you do not want to store your data.
I suggest that you describe here your use-case, and there might be some nicer approach to this problem.
I have a menu with submenus. I initialize all of my main menus in False to load the page to say that the sub menus are not open. When I click on a menu to display the sub-menu, I true it to say that the sub-menu is open
componentWillMount (){
{this.props.routes.map((prop, key) => {
if(prop.subMenus != null){
this.setState({
[prop.code]:false
});
}
})}
}
As the other said you can save routes state in localStorage (it's like cookie, but better)
To save something with localstorage
localStorage.setItem('routes', JSON.stringify(this.props.routes))
And in the componentDidMount method you can do that
const routesString = localStorage.getItem('routes');
const routes = routesString ? JSON.parse(routesString) : undefined

How to update match.params?

The react app has search page. There are input.
The path is 'search/:query', and by default you see zero results.
If you go to 'search/star%20wars' you will see some results. In componentDidMount() I added if statement to load result if match.params.query is not null.
If I type into search input Spider Man and click submit - I trigger a search and show results. But if you reload page - you will see the result about Star Wars. So how update match.params.query? Or may be there other solution of fix this.
You need to update the history object as well.
What you are doing is altering the history object available to you and calculating the results based on that object. But when you will refresh the page it still holds the original history object.
One way of doing it, you need to push or replace a new route in the history.
Because evert search page is a new page, so if you want the previous pages to stay preserved you should use history.push otherwise history.replace
Implement it like this:
var routeObj = {
pathname: samePath,
state: sameState,
query: newQuery
}
//push it in your history using which ever routing library you are using.
//For Example:
router.history.replace(routeObj);
Note: Do not worry about rendering speed on changing the history. React is smart enough to handle that. Basically whenever you will push a route whose component is already mounted it will not unmount and remount the same component again, rather it will just change the props and will re render it.
The callback for this case will be => componentWillReceiveProps
#misha-from-lviv The way I see your problem statement is that you have two source of truth on is the query params, using which you should update your state, and the other is the default state which is populated from the default value of your filters.
As #Akash Bhandwalkar suggested, you do need to update the route in using the History API. But also you also a need a top-level orchestrator for your application state, which will allow you to read and write to the history api ( change your route ) and also do an XHR / fetch for you to get the results.
How I'd approach this is that I'd start with a Parent component, namely FiltersContainer , which actually does this orchestration to read and write to the url. This Container would have all the side-effect knowledge for fetching and updating the routes ( error handling included ). Now the all the child components ( filters and search results maybe ) will just read the state thus orchestrated and re-render.
Hope this guides your thinking. Do revert here if you need further guidance. 😇
Cheers! 🍻

Where should I load data from server in Redux + ReactJS?

For example I have two components - ListOfGroupsPage and GroupPage.
In ListOfGroupsPage I load list of groups from the server and store it to the state.groups
In route I have mapping like ‘group/:id’ for GroupPage
When this address is loaded, the app shows GroupPage, and here I get the data for group from state.groups (try to find group in state via id).
All works fine.
But if I reload page, I'm still on page /group/2, so GroupPage is shown. But state is empty, so the app can't find the group.
What is the proper way to load data in React + Redux? I can see this ways:
1) Load all data in root component. It will be very big overhead from traffic side
2) Don't rely on store, try to load required data on each component. It's more safe way. But I don't think that load the same data for each component - it's cool idea. Then we don't need the state - because each component will fetch the data from server
3) ??? Probably add some kind of checking in each component - first try to find required data in store. If can't - load from the server. But it requires much of logic in each component.
So, is there the best solution to fetch data from server in case of usage Redux + ReactJS?
One approach to this is to use redux-thunk to check if the data exist in the redux store and if not, send a server request to load the missing info.
Your GroupPage component will look something like
class GroupPage extends Component {
componentWillMount() {
const groupId = this.props.params.groupId
this.props.loadGroupPage(groupId);
}
...
}
And in your action...
const loadGroupPage = (groupId) => (dispatch, getState) => {
// check if data is in redux store
// assuming your state.groups is object with ids as property
const {
groups: {
[groupId]: groupPageData = false
}
} = getState();
if (!groupPageData) {
//fetch data from the server
dispatch(...)
}
}
I recommend caching the information on the client using localstorage. Persist your Redux state, or important parts of it, to localstorage on state change, and check for existing records in localstorage on load. Since the data would be on the client, it would be simple and quick to retrieve.
The way I approach this is to fetch from the server straight after the store has been created. I do this by dispatching actions. I also use thunks to set isFetching = true upon a *_REQUEST and set that back to false after a *_SUCCESS or *_FAILURE. This allows me to display the user things like a progress bar or spinner. I think you're probably overestimating the 'traffic' issue because it will be executed asynchronosly as long as you structure your components in a way that won't break if that particular part of the store is empty.
The issue you're seeing of "can't get groups of undefined" (you mentioned in a comment) is probably because you've got an object and are doing .groups on it. That object is most likely empty because it hasn't been populated. There are couple of things to consider here:
Using ternary operators in your components to check that someObject.groups isn't null; or
Detailing in the initialState for someObject.groups to be an empty array. That way if you were to do .map it would not error.
Use selectors to retrieve the list of groups and if someObject.groups is null return an empty array.
You can see an example of how I did this in a small test app. Have a look at specifically:
/src/index.js for the initial dispatch
/src/redux/modules/characters.js for the use of thunks
/src/redux/selectors/characters.js for the population of the comics, series, etc. which are used in the CharacterDetails component

Resources