Project that I'm working on is written in such pattern: each component has index.js with redux mappings and page.js which accepts mappings via props and renders a view.
Component can be huge, it can contain long chains of other smaller components.
Imagine how each component in the chain accepts around 30 props - redux data and redux actions - and then pass them into other components via chain, other components pass them into another components.
Though it's very typical redux situation, I can't live with it and trying to avoid it all the time. I'm trying to use useSelector and useDispatch to work with store, instead of passing dozens of properties I add useSelector right in the place where data is needed.
And all seems to be fine except tests.
I wrote utils for test to mount components inside redux Provider, utils to easily mutate store and to check history of dispatch.
But tests feels more complicated now then before when they just passed props.
It feels that I'm the only person who tries to write tests using redux store.
Is it good idea or bad? Do you know some articles of how to use useSelector and useDispatch and write tests at the same time?
For testability, look into the Container & Presentation Component Pattern. What you basically do is create a component that is purely presentational and maintains next to no state. Everything it displays, it'll get via props. This will let you test the visual elements without worrying about the store.
Next, create a container component that does all of the redux stuff, e.g., fetches, dispatches, etc. and it'll pass the relevant state as props to the presentational component. This will keep your test file size in check and allow you to focus your tests towards different goals while keeping them much simpler. So, your directory will look something like,
coolComponent
coolComponent.js (presentational component)
coolComponent.test.js
index.js (container component)
index.test.js
Testing the store is important as the store and redux determines your data flow. If your data flow doesn't work, it doesn't matter if you have 1000 tests passing elsewhere. You app will fail.
Keep in mind when testing the components that sometimes it doesn't make sense to test the components on their own. If they're only ever used with a parent, I think you can get good coverage testing them with the parent, so don't double your efforts going overboard with testing that way.
Related
I am developing using Presentational & Container design pattern.
Here I have one question.
If all components are connected to the global store, there is no need to divide the components into Presentational components and Container components.
Is there any reason to divide them?
The reason I inquire about this is that if you develop using the Presontational Component & Container Component design pattern, you have to pass props from the parent component to the child component, and I think this is a part that can lower development productivity and cause errors...
So I would like to connect all the components to the global store and use them.
What other problems are there in this case? For example, performance...
"Presentational components" and "Container components" are a pretty old concept that stems from the pre-hooks area. It was nice do have presentational components that could be tested purely on their own - but with the rise of hooks, React components generally house a lot more logic and in most cases, this is fine.
Redux is still a good way of generally moving most logic outside of components - even without a distinction between "presentational" and "container" components. See the Style Guide on this topic
I'd generally recommend reading the Redux Style Guide and if you are still using old-style Redux with connect and switch..case reducers, also read why Redux Toolkit is how to use Redux today
If all components are connected to the global store, there is no need to divide the components into Presentational components and Container components.
First off, any component can be connected to the store (as in, the redux context is available), but you don't need to access state or dispatch actions in every component. So I'd define "connected component" as one that actually uses state or actions.
Is there any reason to divide them?
I'd recommend to simply read state or dispatch actions if and where you need to.
This design pattern from the early redux days is honestly not super helpful and got watered down a bit with hooks and function components (quicker to throw in a useSelector than setting up a connect()ed component).
In some cases you can separate abstract UI logic from global state. As #pailhead pointed out in a comment above, there could be a UserStatus component that defines two different colors for logged in vs. logged out - without being connected to redux. Instead a <UserStatusIndicator> is connected to redux and passes props.isLoggedIn, which is read from state.user.isLoggedIn, to the <UserStatus> component. Imagine the UserStatus component is also reused in an admin panel list that shows the current status of all users in the system - so it's rendered 50 times but independent from redux.
you have to pass props from the parent component to the child
component
Definitely don't do excessive prop drilling, connect these children to redux instead.
I don't quite understand Dan Abramov's 2019 comment in his article Container vs Presentational component.
Is it true that we should still make component Presentational component so that we can re-use them? If we start making components that have an app state, then this component cannot be re-used easily, because then having two of such component on the same page will let them interfere with each other.
So the component can have state, but only "component state", such as whether the comment box is expanded or not, or even the current text in the comment box, etc. It should not be tightly coupled with a certain state in the app. Because then we cannot really re-use this component, unless it is for sure a singleton in the whole app, which mean there is no re-use.
So does it really mean we should write our components so that it is re-usable, and let other component pass in the App data as "props"? This way, we can re-use our components just by passing in different props (and dispatch) from the outer container.
So for our re-usable component, it doesn't matter it is Container or Presentational or combined, just as long as a even higher container can pass it the props and dispatch and let us re-use the component. Is that what Dan really means?
IMHO this statement was more about granularity and separation of biznes logic ... container usually manages some app specific dataset with some set of presentational components.
Of course container component can be reused, too. F.e. list of user followers can use common Apollo client (graphql) as long as our backends are composed using the same (shared) schema (by microservices/federation). It can be freely used in different apps and by using render props we can change its look/behaviour.
It's about reusability in general, it's much easier for stateles (presentational) and locally managed state components (class or functional).
I have an app structure like this with redux:
app
|-router
| — dashboard container
| — — dashboard component
| — setting container
| — — setting component
Consider the two containers, if I need to get translation text from the store which way should I used?
(1) Connect to redux store on router and pass down to containers by props
(2) Each of the container connect to redux store individually
As Dan Abramov, author of redux says in this issue
Both approaches of passing props down to children or connecting them to the store are appropriate, however having nested connect() components is actually going to give you more performance. The downside is they're slightly more coupled to the application and slightly harder to test, but that may not be a big issue.
He has also articulated a nice rule of thumb to follow on reddit
https://www.reddit.com/r/reactjs/comments/4azg7q/using_redux_how_do_you_handle_subcomponents_of_a/
I do it this way:
Start by using one container and several presentational components
As presentational component tree grows, “middle” components start to pass too many props down
At this point, I wrap some leaf components into containers so that “middle” components don’t need to accept and pass down props that are completely unrelated to them
Repeat
He has even tweeted regarding this:
Try to keep your presentation components separate. Create container components by connecting them when it’s convenient.Whenever you feel like you’re duplicating code in parent components to provide data for same kinds of children, time to extract a container.
So in simple words:
You can use connect() at any level. Doing so makes the component smart, since it knows where its props come from. A dumb component just has props, and they could come from anywhere. A smart component is coupled to redux; a dumb component is not.
if the translations are dispatched from router then use connect if they are not fetched or updated by dispatching an action and will never update passing them is better.
I am new to react and redux. I have a scenario where there are nested components like this.
A > B > C > D
There is a property used in A component and it will be used in D component. So, I have two approaches:
Get state from redux store in component A and then pass it along as props to all it's child components even though it will be used only in D component.
I should connect to redux store in component D and fetch that property from there.
What is the correct approach?
As Dan Abramov, author of redux says in this issue
Both approaches of passing props down to children or connecting them
to the store are appropriate, however having nested connect()
components is actually going to give you more performance. The
downside is they're slightly more coupled to the application and
slightly harder to test, but that may not be a big issue.
He has also articulated a nice rule of thumb to follow on reddit
I do it this way:
Start by using one container and several presentational components
As presentational component tree grows, “middle” components start to pass too many props down
At this point, I wrap some leaf components into containers so that “middle” components don’t need to accept and pass down props that are
completely unrelated to them
Repeat
He has even tweeted regarding this:
Try to keep your presentation components separate. Create container
components by connecting them when it’s convenient.Whenever you feel like you’re duplicating code in parent components to provide data for same kinds of children, time to extract a container.
So in simple words:
You can use connect() at any level. Doing so makes the component smart, since it knows where its props come from. A dumb component just has props, and they could come from anywhere. A smart component is coupled to redux; a dumb component is not.
UPDATE: react-redux v7 and above
The same concept applies to useSelectors too. You can receive data in a container component and pass on to your presentational components, if multiple of its children make use of the same data
If however the data used by the children is different, you can choose to use useSelector individually within the child component. This will make sure that only those components re-render which actually need to
I would suggest if you are already using redux in your app then set the property in the redux store and fetch it in the component D.
But if the work flow is really simple and all the data is fetched from a single source per view, you can avoid redux as it is for complex state management.
What's the difference between A) using the connect method of connecting state and dispatch function to a component and B) simply having functions in a component that do the dispatching as well as accessing the store as a global directly?
(Not ES2015)
Quoting from the Redux FAQ on importing the store directly:
While you can reference your store instance by importing it directly, this is not a recommended pattern in Redux. If you create a store instance and export it from a module, it will become a singleton. This means it will be harder to isolate a Redux app as a component of a larger app, if this is ever necessary, or to enable server rendering, because on the server you want to create separate store instances for every request.
I also recently wrote a Reddit comment on why you should use React-Redux and connect instead of talking to the store directly. Quoting that:
First, while you can manually write the code to subscribe to the Redux store in your React components, there's absolutely no reason to write that code yourself. The wrapper components generated by React-Redux's connect function already have that store subscription logic taken care of for you.
Second, connect does a lot of work to ensure that your actual components only re-render when they actually need to. That includes lots of memoization work, and comparisons against the props from the parent component and the values returned by your mapStateToProps function for that component. By not using connect, you're giving up all those performance improvements, and your components will be unnecessarily re-rendering all the time.
Third, by only connecting your top-level component, you are also causing the rest of your app to re-render unnecessarily. The best performance pattern is to connect lots of components in your app, with each connected component only extracting the pieces of data it actually needs via mapStateToProps. That way, if any other data changes, that component won't re-render.
Fourth, you're manually importing the store into your components, and directly coupling them together, thus making it harder to test the components. I personally try to keep my components "unaware" of Redux. They never reference props.dispatch, but rather call pre-bound action creators like this.props.someFunction(). The component doesn't "know" that it's a Redux action creator - that function could be a callback from a parent component, a bound-up Redux action creator, or a mock function in a test, thus making the component more reusable and testable.
And finally, the vast majority of apps built using React and Redux use the React-Redux library. It's the official way to bind the two together, and doing anything else will just confuse other developers looking at your project.
For more info, see:
The Redux FAQ section on performance
The Redux FAQ entry on connecting multiple components
The Redux FAQ entry on components re-rendering too often
The Redux FAQ entry on importing the store directly
The Redux Performance section of my React/Redux links list
And finally, my blog posts Practical Redux, Part 6: Connected Lists, Forms, and Performance and Idiomatic Redux: Why use action creators?.
Source: I'm a Redux maintainer, and author of the Redux FAQ.