Prologue
Props are an integral part of every React application. It is used to pass data from the Parent Component to the child Components.
However, it is just one of many ways that React uses to pass data.
Props is a parameter passed inside a Component
for Typescript, there are 2 things to do with 1 Props:
- export interface received by the Component
- use that interface in Component
1 2 3 4 5 6 7 | <span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">WelcomeProps</span> <span class="token punctuation">{</span> name <span class="token operator">:</span> string <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">const</span> Welcom <span class="token operator">=</span> <span class="token punctuation">(</span> props <span class="token operator">:</span> WelcomeProps <span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token operator"><</span> h1 <span class="token operator">></span> Hello <span class="token punctuation">,</span> <span class="token punctuation">{</span> props <span class="token punctuation">.</span> name <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> h1 <span class="token operator">></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
a single strict rule:
All React props must behave like pure functions:
pure functions are those that do not change the input parameters: function sum(a, b) { return a + b; }
these functions will not change the result with the same inputs
Implicit functions are common with functions whose props are Object, because Object can change the value of its parameters after exiting the function:
1 2 3 4 | <span class="token keyword">function</span> <span class="token function">withdraw</span> <span class="token punctuation">(</span> <span class="token parameter">account <span class="token punctuation">,</span> amount</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> account <span class="token punctuation">.</span> total <span class="token operator">-=</span> amount <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Because a Project can change over time, props alone are not enough, React creates State to solve the problem of changing parameters in each Component
Passing props in components
Remember that components can accept unlimited props, including primitives, React Components or functions.
You can pass data from one Component to another by passing it as an attribute in the HTML element as follows:
1 2 3 4 | <span class="token operator"><</span> Welcome tenProps1 <span class="token operator">=</span> <span class="token string">"giatri"</span> tenProps2 <span class="token operator">=</span> <span class="token string">"giatri2"</span> <span class="token operator">></span> Giá trị của props <span class="token punctuation">.</span> children <span class="token operator"><</span> <span class="token operator">/</span> Welcome <span class="token operator">></span> |
So in the Welcome component, the value of props will be an object including the values passed:
1 2 | <span class="token punctuation">{</span> tenProps1 <span class="token operator">:</span> <span class="token string">"giatri"</span> <span class="token punctuation">,</span> tenProps2 <span class="token operator">:</span> <span class="token string">"giatri2"</span> <span class="token punctuation">,</span> children <span class="token operator">:</span> <span class="token string">"Giá trị của props.children"</span> <span class="token punctuation">}</span> |
When you pass a value inside a tag it will be the value of the chirlden property in the props object as shown above.
Get props in components
Get props in functional components by specifying parameters in the function.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">"react"</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">Welcome</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">props</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> props <span class="token punctuation">)</span> <span class="token comment">//Giá trị của props</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div <span class="token operator">></span> <span class="token operator"><</span> h1 <span class="token operator">></span> Xin chào <span class="token punctuation">{</span> props <span class="token punctuation">.</span> tenProps1 <span class="token punctuation">}</span> <span class="token operator">!</span> <span class="token operator"><</span> <span class="token operator">/</span> h1 <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> Welcome <span class="token punctuation">;</span> |
Combine components
Some components don’t know about their child components when they are created. This is very common with components like Sidebar and Dialog that act as generic “boxes”. In this case I recommend using props.children to pass child elements directly to the render of these components:
1 2 3 4 | <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div className <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token string">'FancyBorder FancyBorder-'</span> <span class="token operator">+</span> props <span class="token punctuation">.</span> color <span class="token punctuation">}</span> <span class="token operator">></span> <span class="token punctuation">{</span> props <span class="token punctuation">.</span> children <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> |
It allows other components to pass child elements more dynamically by nesting JSX together:
1 2 3 4 5 6 7 | <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> MyComponent color <span class="token operator">=</span> <span class="token string">"blue"</span> <span class="token operator">></span> <span class="token operator"><</span> h1 <span class="token operator">></span> Welcome <span class="token operator"><</span> <span class="token operator">/</span> h1 <span class="token operator">></span> <span class="token operator"><</span> p <span class="token operator">></span> Thank you <span class="token keyword">for</span> visiting my Website <span class="token operator">!</span> <span class="token operator"><</span> <span class="token operator">/</span> p <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> MyComponent <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
sometimes you may need a lot of space in a component. In such a case you can create your own conventions instead of using a child:
1 2 3 4 5 6 7 | <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div className <span class="token operator">=</span> <span class="token string">"SplitPane"</span> <span class="token operator">></span> <span class="token operator"><</span> div className <span class="token operator">=</span> <span class="token string">"SplitPane-left"</span> <span class="token operator">></span> <span class="token punctuation">{</span> props <span class="token punctuation">.</span> left <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token operator"><</span> div className <span class="token operator">=</span> <span class="token string">"SplitPane-right"</span> <span class="token operator">></span> <span class="token punctuation">{</span> props <span class="token punctuation">.</span> right <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
use it:
1 2 3 4 5 6 7 | <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> SplitPane left <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator"><</span> Contacts <span class="token operator">/</span> <span class="token operator">></span> <span class="token punctuation">}</span> right <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator"><</span> Chat <span class="token operator">/</span> <span class="token operator">></span> <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
This method may remind you of the concept of “slots” in other libraries but there is no limit to passable parameters like props in React.
Specialization
We sometimes think of components as “a special case” of other components. For example, we can say that WelcomeDialog is a special case of Dialog. In React, we can combine multiple “special” components to create a common component and customize it with its own props:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">function</span> <span class="token function">Dialog</span> <span class="token punctuation">(</span> <span class="token parameter">props</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> MyComponent color <span class="token operator">=</span> <span class="token string">"blue"</span> <span class="token operator">></span> <span class="token operator"><</span> h1 className <span class="token operator">=</span> <span class="token string">"Dialog-title"</span> <span class="token operator">></span> <span class="token punctuation">{</span> props <span class="token punctuation">.</span> title <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> h1 <span class="token operator">></span> <span class="token operator"><</span> p className <span class="token operator">=</span> <span class="token string">"Dialog-message"</span> <span class="token operator">></span> <span class="token punctuation">{</span> props <span class="token punctuation">.</span> message <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> p <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> MyComponent <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">WelcomeDialog</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> Dialog title <span class="token operator">=</span> <span class="token string">"Welcome"</span> message <span class="token operator">=</span> <span class="token string">"Thank you for visiting our spacecraft!"</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Practical examples
For example, a Page component passes the user and avatarSize to some level downgrading so that the Link and Avatar components can read:
1 2 | <span class="token operator"><</span> Page user <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">}</span> avatarSize <span class="token operator">=</span> <span class="token punctuation">{</span> avatarSize <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> |
1 2 | <span class="token operator"><</span> PageLayout user <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">}</span> avatarSize <span class="token operator">=</span> <span class="token punctuation">{</span> avatarSize <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> |
1 2 | <span class="token operator"><</span> NavigationBar user <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">}</span> avatarSize <span class="token operator">=</span> <span class="token punctuation">{</span> avatarSize <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> |
1 2 3 4 | <span class="token operator"><</span> Link href <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">.</span> permalink <span class="token punctuation">}</span> <span class="token operator">></span> <span class="token operator"><</span> Avatar user <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">}</span> size <span class="token operator">=</span> <span class="token punctuation">{</span> avatarSize <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> Link <span class="token operator">></span> |
Each line is a Parent Component and has a call to the Component directly below it. It might feel redundant to pass the user and avatarSize through multiple levels if only the Avatar component really needs it.
The problem is even bigger if the Avatar component needs more props from the top layer, you have to add all those props in all the middle layers.
One way to get around this without using context is to pass the Avatar component itself, this way intermediate components don’t need to hold the user or the avatarSize props:
1 2 3 4 5 6 7 8 | <span class="token keyword">function</span> <span class="token function">Page</span> <span class="token punctuation">(</span> <span class="token parameter">props</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> user <span class="token operator">=</span> props <span class="token punctuation">.</span> user <span class="token punctuation">;</span> <span class="token keyword">const</span> userLink <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token operator"><</span> Link href <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">.</span> permalink <span class="token punctuation">}</span> <span class="token operator">></span> <span class="token operator"><</span> Avatar user <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">}</span> size <span class="token operator">=</span> <span class="token punctuation">{</span> props <span class="token punctuation">.</span> avatarSize <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> Link <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token operator"><</span> PageLayout userLink <span class="token operator">=</span> <span class="token punctuation">{</span> userLink <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
then the code will look like this:
1 2 | <span class="token operator"><</span> Page user <span class="token operator">=</span> <span class="token punctuation">{</span> user <span class="token punctuation">}</span> avatarSize <span class="token operator">=</span> <span class="token punctuation">{</span> avatarSize <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> |
1 2 | <span class="token operator"><</span> PageLayout userLink <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> |
1 2 | <span class="token operator"><</span> NavigationBar userLink <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> |
1 2 | <span class="token punctuation">{</span> props <span class="token punctuation">.</span> userLink <span class="token punctuation">}</span> |
With this code, <Link>
will be created at <Page>
always, the child components no longer know what changes in <Link>
.
This inversion of control can make your code clearer in many cases by reducing the number of props that need to be passed throughout your application and allowing control to reach the root component.
However, this is not a good choice for all cases, moving the complexity higher up in the component tree makes higher-level components complicated and forces the components to be more complicated. lower-level components become too flexible.
Sometimes there is duplicate data that is used by multiple components in the component tree, and at different levels. The Context now allows you to “broadcast” such data, and transmit it to all the underlying components.
Context is outdated now, so we can use Redux instead