React - is it wrong to rerender on props change? - javascript

I have json data I get from database in my simple todo list. I have implemented it so I save these data in props and then pass it onto child components. The only way I use state right now is for filtering these data: (show all, show completed, show ongoing).
Now I want to add backtrace to database. Which means adding new items/deleting old ones. The problem is if I implement add button that adds new task to database it will not rerender the application automatically so the change wont show until I refresh the page.
So my question is. Is it correct approach to force call render in React in this situation? Or should I completely revamp the application and change the array of tasks which is prop now to state and update it at the same time while I update the database?
Thanks for answer :)

I think that need to rerender is based on whether the component should be updated or not. And in this case it should since there is new item.
As for moving to state I don't think that you need to rewrite your component to use state instead of props. You can just implement shouldComponentUpdate and check if list of todos has changed (be careful to not mutate the todo list prop!).
If you're using React.PureComponent then you don't need to bother with shouldComponentUpdate, it will update automatically on any state or props change

Using your data (in Json) as props for your React Function Component or Class Component is perfectly fine and frequently used.
Assuming that you use a Class Component (or just you React App component) and keep the data up-to-date in the state of that component, you could add member functions to add and delete the items. I would recommend to keep user interface in sync with your user actions, and maybe have a form submit handler to process all database changes (if this is feasible for your situation).

Related

React - Fetch data in the parent or inside its childs?

Note: I am not using Redux (only Context + Hooks)
Introduction
I have a screen "Profile", that renders a TabView.
This TabView, renders two components as scenes:
UserPosts
UserInformation
My Profile screen, fetches the user data from my database, as it has to render some stuff that depends on this data.
Also, it is passed down via props to my "UserInformation" component, as it just renders the user data. So... there is no data fetching in my "UserInformation" component.
In the other hand, my "UserPosts" component is responsible of fetching the user posts, as it renders a component "UserPostsGrid" which renders those posts with pagination (endless FlatList).
Note: Data fetching is performed in custom hooks, not directly inside
the component (also, those hooks manages the respective stateful
data).
Problem
The main problem comes when I need to refresh the user data and the user posts when the user pull-to-refresh inside the "Profile" screen.
There is no problem for this screen to fetch the user data, as I have said before the "Profile" screen is responsible of that action.
But the posts are the stateful data of my child...
Multiple solutions?
I have thought to solve this problem using the current solution:
React.forwardRef() + React.useImperativeHandle()
With this, I will be able to pass a ref to my "UserPosts" and use imperative programming for accessing the data fetching method: userPostsRef.current.refreshPosts();
I have never seen other scenarios for solving this problem like this, but it should work. Instead, other coders implement a ContextProvider to handle all the data and be able to access it wherever they want. The main problem I notice here is: extra memoization + extra re-renders.
Another solution might be to implement all the data fetching inside the parent, and pass the respective callbacks via props to the child. The main problem I notice here is that we are creating some kind of "God Component" and we might have multiple lines of code...
My question
Is it an anti-pattern or a bad practice to implement the first solution ?
To answer your question:
Yes, it is an antipattern in React, because React is built around the idea that you specify the state of your app, and then let React render the components depending on this given state.
If something should change, you are supposed to specify a new state that represents the difference to the old state.
Refs should only be used if there is no other option. Basically, if you use Refs you say "I don't want to use React for this specific part of my code".
As always, imperative code using refs should be avoided in most cases
To use state instead of commands:
I would approach this kind of problem by trying to translate the imperative action into some state of information.
The user says "get me the latest data", that is a command, i.e. imperative.
Now I would ask "what is the user telling me about the state of the data right now ?"
An answer would be "this data is not up to date anymore", and this can be expressed as a state, and passed around via props.
A solution for your problem:
E.g. you might use a prop needsUpdate, which is set to true when the data should be updated, and to false when fetched.
To avoid unnecessary renders (setting needsUpdate = false would cause another call of e.g. useEffect, even if nothing happens there), I probably would use a child component prop like latestUpdateRequest and a child state like latestUpdate which holds a counter or timestamp, and then compare if(latestUpdateRequest > latestUpdate).
To inform the parent about the change just pass a callback function like dataUpdated() as a prop.

Redux: How to manage conflicting state across routes?

In my application, on route1 I get a list of items from an api call. The component is connected and the items are saved in redux store and passed to the component as props. Now, on route2 I again need this list of items.
For now, i am fetching the list of items again when we move to route2
Does it make sense to reuse the list of items which is already in global state? If yes, how do we decide when it is stale and when to refetch?
If it doesn't make sense to reuse the list of items, should i clear the list when moving away from route1/when loading route2? Because otherwise, my component in route2 cannot start with an assumption that the list would be empty on load, and I would have to put an extra check for whether or not the list is empty. This would make my component kinda aware of the global state structure.
Does it make sense to name these pieces of state differently, so that one doesn't interfere with the other? This doesn't seem right, as both the functionalities are same, and we wouldn't want to increase the size of global state for this.
is redux or any global state management library not a good choice for handling this kind of state? This seems like state local to a route, which should not interfere with local state on another route, so it is not truly global. Should we only keep global state in redux ?
I have previously been using useReducer with axios calls to do this. The state in that case was local to each of the components/route, so they didn't have to care about whether or not the state has been populated by someone else. This approach had problems with sharing state between routes, but local state problem was handled well.
In this situation, I think you should store the item list in the global state as readonly data. When a route component initialize, it should clone this item list from the global state and store it as local state.
This way ensures that the item list data is shared across multiple components without having to refetch it for every route while each component can choose to do whatever they want with its local data without affecting other components. Also, when the component unmounts, it will clean up its local state so you don't have to worry about memory leak due to multiple copies of the item list.
I have been in this situation and the approach I used was as follows:
Keep one state for both routes. Since that's the only point of
keeping a single source of truth across the application. If we want to keep a separate list for both routes then we can directly use state which I think is not required here.
Fetching of the list should be done in componentDidMount of
the first route and in route 2 you can again fetch the list and compare the data based on some checksum if we have new data or not if we have new data update the original list. In this, we don't have
the problem of stale data too.
Maybe you can have a different use case but check if this approach works.

React Router - Mobile First: Don't unmount the current component until the new one is ready?

TL;DR: When transitioning pages with react-router v4, how to keep current component until the new component is ready i.e. it made its ajax calls and fetched necessary data to display?
Currently react-router v4 works like this:
User clicks link
Route changes
Unmount the current component & mount new matching component
Ajax calls made to fill components data
However if we think of mobile-first approach, the workflow should be:
User clicks link
matching component is mounted, hidden, being initialised via its ajax calls in componentDidMount/updated
initialisation done, component and its data are ready
Change URL, unmount the old component and display the new one.
Changing URL could be after user clicks the link or after the new component is ready.
By using redux we can achieve this by:
Make all components stateful, have componentXLoad() actions for each
Instead of route change, fire these actions and inside the actions change the url after the component's data is fetched
However, I believe we do not need to go all the way and make every component stateful and generate lots of actions for every page transition.
Idea I have was:
User clicks the link
Route changes, new component is being initialised
If new component's render() method returns null, don't unmount the current component
When new component is ready, rendering non-null, unmount the old component and display the new one.
I believe this is theoretically possible.
Is there already a library doing this?
Or is this something easy to do with React Router v4's API?
Also:
What are the implications on this on SEO, any chance Google considers as cloaking to penalise the site?
I've found this library https://github.com/faceyspacey/redux-first-router, seems it can do this by making everything stateful, but again for something this simple seems like an overkill to switch to that.
Thank you
If you need it only for this specific case I'd say try this:
From component A fetch the data you need to fetch (maybe show some indicator while fetching)
Transition to component B passing the data as custom props
Component B renders initially with the data

React, best way to deal with fetching when navigating between routes containing same component

Basically, I would like to fetch the latest data every time user navigates to different tab. For example, I have summary page and details page for customers, which both includes the same customers container component. I'm doing my initial fetching in componentDidMount function, which doesn't get triggered in route change, as component stays in the DOM. Component necessarily doesn't receive new props either.
One solution would be to customize router link with fetch action being dispatched based on the href, but I'm pretty sure this is an anti-pattern, as it complicates code a lot, is hard to maintain, mixes concerns, and may result in a double fetch if target route didn't contain the component already.
Does React provide any lifecycle hook or mechanism that allow me to fetch in response to navigation event alone?
I'm using React-router-component and Redux.
As far as I understand both tabs happen to contain the same component and so React re-uses the DOM and doesn't create a new component instance, right?
You can use React's special key prop to force React to replace a specific instance.
E.g.:
<div className="tab-content>
<div key="tab-1" className="shared-component-across-tabs">
shared-component-across-tabs
</div>
</div>
And in the second tab you'd use key="tab-2".
Otherwise your route handler component will receive props.location.pathname from the router. You can also implement componentWillReceiveProps and check if the pathname has changed and re-fetch the data in that case.

Should data go in a redux state tree?

I am a bit lost on what to keep in the state tree of Redux.
I saw two conflicting statements on what to store in the state tree(s).
React doc tell us that only user input should be stored in state trees.
The original list of products is passed in as props, so that's not state. The search text and the checkbox seem to be state since they change over time and can't be computed from anything. And finally, the filtered list of products isn't state because it can be computed by combining the original list of products with the search text and value of the checkbox.
Redux doc tells us that we often should store UI state and data in the single state tree:
For our todo app, we want to store two different things:
The currently selected visibility filter;
The actual list of todos.
You’ll often find that you need to store some data, as well as some UI state**, in the state tree. This is fine, but try to keep the data separate from the UI state.
So React tells that we should not store data (I am talking about data of the todos) and, for me, Redux tells the opposite.
In my understand I would tend on the React side because both React and Redux aims to predict a UI state by storing:
all what can't be computed (eg: all human inputs) and are part of the UI:
checkbox value
input value
radio value
...
All minimal data that could be use to build a query and send it to the API/database that will return the complete user profil, friends lists, whatever...:
user Id
creation dates range
items Ids
...
For me that excludes all database/API results because:
that stands on data level
could be computed by sending the right (and computed by pure reducers) query.
So what is your opinion here?
React documentation about the View Component state, but Redux documentation about the Application state. So, there is no conflicts between definitions.
If we talk about Redux - you make all your components without state (and transform stateless root component to stateful with help of react-redux's connect function). If you have large response from the server and you show your data with pagination / filters, you can treat your application state as what you see on screen and not put all data in Redux store, only what you need to render (for example, 100 rows to show page and total number of rows to show pagination). There is no restriction for this. The whole data you can put into another place. For example, in another data container in web-worker (I make a full request in web-worker and fetch from there only needed data to display).
Added after question edited:
The original list of products is passed in as props, so that's not state.
In that example, the reason why list of products isn't state - it's already in props. It means that the one of parent components have this as state.
I feel that the problem is that originally Redux was pushed really hard, and some people were so purists, that they argued for separating everything to Redux and re-rendering the whole application on every change. And then we ended up with this response of the creator, which actually only added a confusion, because redux was and still is a de-facto standard for new react applications, and a lot of tutorials assume it.
So, I feel that people are pressured from each side, and often they do some things without real understanding why they should (especially newcomers creating constants, actions and reducers). So, for those who read it, please start without redux, and keep it just in local state (but try to keep in some component like DataContainer).
For those who need redux, rule thumb is to put all async data (so all requests go through redux), and data which is needed for independent components. If components obviously located nearby, keep it in a local state and pass as props.
Redux is a very helpful library, but it's power is needed only after you start to have at least several routes, different query options and some complex UI. Before that there is a good chance that you are overengineering (but, of course, if you are sure that you will exceed this size, feel free to start with Redux). And again, you'll literally never will want to put your slider or small dropdown position in the store -- react's state serves perfectly for it.

Categories