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.