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.
Related
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.
I'm learning react and having some trouble getting my head around some code design. I have a Session component that has several Activity components in my project. In the Session component I also have a Timeline component and I need to show the total duration of all of the events. So I need to have the start time for each of the Activities in the Session component and the end time of the last. I know of "lifting state up", but I find it a little strange in my OO way of thinking from C++ to store the data in the parent and not where it "belongs". What if I later need some other data from the activity.. Then I would lift parts of the Activity data up and store some of it in the Activity.. seems quiet messy?
I thought of also having a ActivityData object and store a list of them in the Session and pass that in to each of the activity to display it there. Another way I thought about would be to have a SessionModel object and a ActivityModel object and have this seperate from the component all together and pass these models in for rendering in the component.
I am also just getting into typescript and moving my code into that. I was thinking that I could define a custom ActivityData type in the same file as the Activity component and still store the list in the Session, but then at least its more explicit that the data belongs to the activity.
What is the right (or React) way of doing this?
You are right in your thinking and yes it isn't necessarily an OO way of doing things but more of a functional programming approach
Each component can have state, and it should store that state in it's own component. If that state is then needed by another component you can pass it down as a prop. However, if it isn't a child component that needs it then like you said you should lift state up.
The next problem happens when your app starts to grow. So then you need to make some choices. You should split your components up so they don't get too big. You can have some more logical components and then have some presentational components that don't handle logic but essentially just take props and render the views from you.
However, your app is still growing so at this point you might want to invest some time in introducing a state management tool to your app. React has the context
api built into so you can use that. or you could use a library likeredux. Redux is particularly good at abstracting state to a "global" store and you each component can "connect" to the store to access the state. Really good for apps where you have lots of shared state and lots of components need to know about similar pieces of state
In terms of Typescript then it's certainly a wise idea to include that as the language is heading that way. You can keep types in the same file or keep them in the same directory but have a .types.ts file that you import into your code and declare your types/interfaces in there
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.
I am trying to build a new react app and I have some question about the architecture.
I have 2 components:
Adress
CreditCard
Each Component has several inputs and api functions to get the data and also to update it.
When I use both components in one Page, how can I trigger an update for both components?
Page
Address
CreditCard
Submit Button
The Submit Button should trigger the Update function and wait until both are finished.
I tried to give a function from the Page to the child components via props, but I think this is the wrong way.
Redux
For complex interactions between different components you can use redux. Redux holds the state of your entire application and can only be updated with actions.
An action will trigger a reducer. A reducer returns a new version of the state with what ever update you set there.
Treat redux some what like a database, do not store things multiple times and keep a good separation of concerns. There's an awesome library that lets you aggregate and manipulate data called reselect.
Reselect
This library will let you take several parts of the store and combine them for your needs in any given scenario.
Conclusion
To conclude, these's libraries are part of a stack I've been using for about 2.5 years (reselect only 1 year). I've found them very powerful for handling complex data. That being said there are other options like graphql, apollo or relay.
In my application, there are views with dependencies. For example, in one view a user could select an item from a list (generated on the server), and in next view the user would perform operations on the item. The item is passed to the second view in props. I'm moving to using react router, but there are some difficulties:
I can't use props for transferring data anymore. What would be a preferred way to pass data? Do I have to use redux?
Users can navigate from any view to any other view by directly using url. However, some transitions don't make sense: e.g. user navigates to item editing view from somewhere else, and therefore does not have an item selected. Is there a way to limit allowed transitions?
This is a very broad question, but I'll take a stab at it.
Can you use Redux? Sure, Redux is good for centralizing your state which can easily be shared among your components. As far as limiting the url's they have access to, I would use your reducer to look at your current state, if you're using Redux and if data is not there, meaning they should not be at this step, use a javascript redirect to where they should be instead.
Finally, you don't have to use Redux to share data between components this could be done by setting global variables your components can access, but cross component communication is where Redux shines.