As a react web app gets complex, some components have a number props, which is less readable, harder to expect what this component does, and tedious to add lots of prop types checks and pass props from top-most component to bottom. This is mostly caused by passing flux/redux actions and stores passed from top to bottom.
Is there a great way to reduce the number of props passed in?
There are two solutions I thought of, which are not perfect:
Pass props with a spread operator {...props}. This does reduce writing a number of props and prop type checks, but there may be conflict in naming, so the names of actions/stores should be unique. Another downside is to be extra careful of what props to pass or not to avoid side effects.
Wrapping the component with a container which directly connects actions/stores to the component, in an hoc-fashion. For example, react-redux's connect() can be used. This is cleaner and simpler than Solution #1, but it's hard to write component tests if the component contains a container because of a Redux error.
One example of the error is
Invariant Violation: Could not find "store" in either the context or props of "Connect(Header)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(Header)".
You've described two approaches and their pros and cons pretty well. I'll add a few design considerations to what you've said.
Approach #2 is my preferred approach. Reducing the amount of props passing through the components avoids complexity. There are ways to test components without triggering that error, but I think that deserves its own separate Stack Overflow question, to honest. For now, I'll just say look into shallow rendering, and consider whether you really need to just do unit testing here vs integration testing. If you will also be creating automated tests in something like Selenium, then that can serve as your integration testing, perhaps.
Approach #1 can be improved by making scrupulous use of PropTypes to validate what is being passed through. It's reasonable to leave out the PropTypes checking on the middle components that are just passing ...props through, but the end components (components that actually use the props instead of just passing through) should have really stringent PropTypes declarations. Use Shape instead of Object. Use ArrayOf instead of Array, and basically take all opportunities to be specific in your PropTypes declarations.
Regarding your concern about name conflicts, it sometimes helps to group props together as members of one object and pass that object as a single prop. If the props are conceptually related or have a single destination component, this makes some sense. I still greatly prefer just having more container components (approach #2) since it causes less information to flow through props. And writing good PropTypes for objects with nested members takes a little more time and yields warning messages that take longer to troubleshoot.
People sometimes forget about .state after they start using Redux/Flux, with some purists preferring to send and receive everything through the store. The elegance of the stateless component declaration further biases me against adding .state to a component. But .state is great for tracking ephemeral things like animations and tooltip visibility. Not everything needs to be in the store and pass through props.
Related
can I use Use state instead of redux?
Can I Manage all the state by use state ?
This question has a long and complicated answer, but I would suggest the first port of call would be reading up on the redux docs about how you might want to organise the state within your application.
TL;DR, it entirely depends on your situation, take a look here
Both are valid, but both have cases where they are better to be used, for example if you have a state you want to manage one level below, and is not used by other components on different hierarchy, then passing the state and it's handler as a prop is the better solution.
But some cases are more complex and require a lot of passing down of a prop through the components until it reaches the child that actually uses it, and. the parents of that child do nothing but pass it down, that is a smell of bad code.
Here it is beneficial to have it in a global state where the child can access it directly, without needed all of its predecessors to pass it down as a prop (also known as prop-drilling). Another case where this is useful is when multiple components need to access the same state, in different part of the system. It would be much cleaner to have it stored in a global state available for every component that requires it.
TL;DR, depending on your case, one solution is better than the other. Assess your situation case-by-case.
As I see it, I could make my entire app a presentational/dumb component and then have a single container component pass the appropriate props.
On the other extreme, I could make every single component smart.
So, is there a rule of thumb to decide when to make your presentational component smart?
Quoting from Presentational and Container Components by Dan Abramov:
When to Introduce Containers?
I suggest you to start building your app with just presentational components first. Eventually you’ll realize that you are passing too many props down the intermediate components. When you notice that some components don’t use the props they receive but merely forward them down and you have to rewire all those intermediate components any time the children need more data, it’s a good time to introduce some container components. This way you can get the data and the behavior props to the leaf components without burdening the unrelated components in the middle of the tree.
... This is an ongoing process of refactoring so don’t try to get it right the first time.
I would argue that there should be a nice balance between the size/complexity of the app and the number of smart components (containers).
Consider introducing a container when:
The hierarchy becomes too deep and passing props around becomes a hassle.
It is likely that an entire feature (UI + data mapping) will be moved/re-used across the app.
There's a need to encapsulate a feature for reasons such as the above or any other (versioning, security, lazy-loading etc.)
You identify unnecessary updates as a result of data mapping.
On the other hand, consider sharing a container when:
Multiple components use data of the same nature and form a single unit that might be considered a "feature".
Mapping (mapStateToProps/mapDispatchToProps) becomes repetitive amongst multiple proximate components.
Also, keep in mind that refactoring might be necessary as your app evolves.
I'm working on a part of a React app in which a high-level component creates and passes certain props down through a few layers of components that don't use them to a final component that does.
When validating props with propTypes, is there a good reason to list these props to be checked at every level, going down through the layers? Or is it acceptable to check them only in the final component that uses them?
It seems to me that the former method is redundant; the latter seems to make more sense to me, but I'm curious if there is a reason why I ought to do the former. I haven't seen any discussion on it, which could mean it's an unimportant question, but I'd be interested to know.
I agree with you about if you use props only for dril down for children in the tree, it can be done only once at the leaf components, where you realy use this data. I recently find out that one more place is important for props validation: the components which fetch data from out of app scope, such as backend, because sometimes the structure of the data changes or the data types, then it will be dificult to find which part is broken without props validation.
When passing data between two elements that are very far away from each other in the hierarchy of components, passing data through props can be tedious. In these use cases I've resorted to using Redux just because it is less to keep track of when there is a large amount of components.
What I've done in one little project is to use a closure to encapsulate state and export that variable and consume it elsewhere. I feel this is a an antipattern but it does work.
The way it works is by declaring some variable that is going to be modified within a component. This same variable is the imported from elsewhere and consumed from elsewhere.
Here is a small sample with what I am doing (just pretend there is a large component hierarchy): https://codesandbox.io/s/2R9RvYkN1
So my questions are: is there a better way to achieve the same results? Should we use a Flux implementation for these use cases? Is it ok to just pass props around through a large hierarchy of components?
As you stated yourself, redux solves this problem by providing an "App state" that's global to your app, which allows you to connect any component you want to that state.
Your "closure" is merely a poor-man's Redux, it's also a global state, only it lacks any of the features provided by Redux(unless you write them specifically).
let's CompA needs to re-render based on a click event on CompB, how do you do that automatically with a closure? you'd have to add listeners, check if a relevant state was changed and then re-render.
all these things are done for free by Redux, so I don't see any added benefit(except for not using redux, which can be a benefit in it's own).
If it's that important not to use redux, this can be "fine", yet very dangerous and I'd say it won't scale well.
I have two components, contact form, and input.
At this moment i pass onChangeEvent from contact to input as is described in many tutorials and its works fine - input update his owner state.
But there is way to pass 'this' from contact to input by prop, or context and then I can update owner state without passing onChangeEvent - but is this a good idea?
Is there another option to update owner state without passing onChangeEvent?
I believe you could technically do it, as a React component is a regular javascript object in the end, so you could pass it as a prop.
However, that's not a good idea in general, for various reasons:
It tightly couples the two components together. If you ever want to reuse the input component in another place, you'll need to pass in the exact same state.
Linked to this, it allows manipulation of the internal state of one component, by another component, which is a violation of good OO design.
You are right however, that things tend to become quite verbose when working like this. They also become hard to reason about when one has more complex trees of components passing props and change handlers between them.
One solution to the problem, is employing the Flux design pattern, and namely it's Redux implementation.
In Redux one has a single piece of global state, a plain object, of which components see pieces (sub objects). Components receive this state as props, and just render from it in a simple fashion. There's a set of actions which transform this state, and any component can issue such an action, as a result of user interaction. There's still the concept of "state", but it is reserved for truly local things, such as the state of a form before pressing the save button etc.