Props Recommendations
Published on October 1, 2024
Props are data that is passed to a component from it’s parent component.
Here is a list of tips how to use props properly in React
#1. Avoid prop drilling
Prop drilling means passing props along the tree of React Components
take a look at the following example where <App>
component is passing a prop to <Child>
component, and <Child>
component is passing it to <GrandChild>
component.
this is called prop drilling and it’s considered a bad practice when passing props in React
.
Ways to avoid prop drilling
To avoid prop drilling you can do the following:
- store data in context
In this example instead of passing the prop down the tree of the components, we are creating a context and storing that prop in the context.
- use a state management library
Another way to avoid prop drilling is to use a state management library like Redux
or Mobx
to store the data in a global state.
While in this example I will demonstrate with Redux, Redux
is not the only state management library out there, there are many popular alternatives where each one has it’s pros and cons.
We need to wrap our entire application with the react-redux
Provider
component so that the tree of components can access the Redux
store, and we are doing that in App.js
.
Parent.js
can store data in redux that GrandChild
can access without the need for Props drilling
GrandChild.js
to access the data in redux using useSelector
hook.
#2. Use conventions in props naming
If you look at Material Design and examine the names of the props that you pass to the components, you will notice that the props names are repeating between different components.
For example: variant
, sx
, disabled
, color
, etc.
When you create an application, you have to imagine that you not only creating an application, but you are creating an entire language.
That language reflects how your application looks like: repeating style guide, repeating components and ux.
That language also reflects how your code looks like: reusing functions, class, creating your own libraries, creating toolset that you will use throughout your application.
That language you create enforces you to build your application like lego blocks using good infastructure, and reusing your code.
That same language will apply to certain convention you will have to follow, those conventions will help you reuse your language.
For example in your database, in the tables columns, strive to use similar naming for similar columns, if for example a certain entity (A) contains a name colum, and another entity (B) contains also a name keeping the same conventions will direct you to place the column for B that is named name
and not for examle using in A the column name
and in B the column title
.
Why is it so important to keep naming similar?
In one word Grouping
When creating a software we like to place similar things in a group, from those groups, you can divide it to larger groups, and larger ones and larger ones.
For example maybe you take functions that are relating to each other and placing them in a class, maybe you take that class and combine it with helping function and utilities and place in a library package, maybe you take that library package and combine it with other library packages and create a dependency graph, and from that create different applications.
We love to group things together when developing software, and that is why the names are important.
It’s easier to group things together if a lot of their names are identical to another element.
same applies to the names of the props in your components, try to keep them similar, it will help you organize, inherit, use different patterns, find your DRY
violations.
It will also make your components more understandable to your team.
#3. Prop types, optional and default value
Ask yourself, what is the type of each prop passed? Is that prop required or optional? If the prop is optional does the prop have a default value?
Using TypeScript answering these questions might look like this:
notice how we specify every type of prop that is passed, we also write if that prop is optional or required, and if needed we give that prop a default value in the destructor.
Same rule applies to JavaScript as well where the same code will look like so:
#4. Add comments
Be a team player, document your component including the props that component are getting.
Developers on your team will not simply understand right away how to use your component, help them out with proper JSDocs
Working with JSDoc
will play nicely with your IDE which would be able to present you comment whenever someone hover on that function.
#5. Generic types
Are you using TypeScript
? If so then make sure to use all the power that TypeScript
is giving you to define the exact types for your props.
For example you can use Generic Type <T>
to dictate typescript checks based on a type passed a <T>
.
Let’s take this component as an example that gets an instance of a class and prints one of the properties of the class, according to the props passed to the component:
Notice that FilterKeys
filters only the keys that extends U
, so if I’m passing the object:
then FilteredKeys<typeof a, number>
will return age | meaning
.
The props of this component will get 2 generic types so to call this component we can do the following:
Did you know that most of the components in mui
have a generic type?
But we don’t really need to pass them when using their component right?
Actually in our example we don’t have to pass those generic too, and we could have written the same as:
typescript can infer the generic types based on the other props we are passing, that is why usually you don’t need to pass those generics to mui
.
Generics are just a subset of the power of typescript which I see a lot of experianced developers miss out when writing their components.
#6 To memo or not to memo?
You should understand when you should memoize the props you are passing.
As a general rule you should memoize the props you are passing in the following cases:
- When the component you are transfering the props to is a pure component that is wrapped with
React.memo
, and the components you are passing are non primitives, and you want to avoid rendering this component when the parent renders:
you would memoize the props passed to Child
if you don’t want him to render every time the parent renders.
Child
is wrapped with memo
so it will only render when the props change.
- the props are passed in the child is some dependency array for example in
useEffect
,useCallback
,useMemo
and you don’t want them to run again
notice that I got a prop and I placed that prop in a dependency array, so the parent can control when he is generating that student to avoid expensive calculation in the Child
#7 If you can obtain them, then don’t pass them
At the beginning of Redux
in their docs, they specified a pattern to split your components to Container components and Dumb presentation components.
It’s a pattern that helped Dan Abramov in the past and he wrote about it in this article
In the start of this article you will also see his comment:
Update from 2019: I wrote this article a long time ago and my views have since evolved. In particular, I don’t suggest splitting your components like this anymore. If you find it natural in your codebase, this pattern can be handy. But I’ve seen it enforced without any necessity and with almost dogmatic fervor far too many times. The main reason I found it useful was because it let me separate complex stateful logic from other aspects of the component. Hooks let me do the same thing without an arbitrary division. This text is left intact for historical reasons but don’t take it too seriously.
The era of smart containers and dumb components is not fitting the world of the new components with custom hooks.
Many times I see developers passing props that can be obtained by simply calling some kind of hook.
Let’s examine the following example using NextJS
The <Article />
component will display an article based on the id
taken from the url.
But why exactly are we passing it?
why don’t the article obtain it himself by calling the useRouter()
hook himself?
The more props you will have to pass down the harder it is to understand you component and the objective of each prop, think simple and you will see you component reused more.
Summary
We learn about props very early when we started learning about React
.
Seems like such a simple topic, yet I see so much bad practices and mistakes of even the most experianced React
developers.
Not following best practices for props will lead to more bugs, more renders, components api which is not clear and thus less used by other developers, it’s even effects testing and making your components hard to test.
Hopefully this lesson will improve you React
prop passing ability.