Props! — and how to pass props to components in React . (part_1)

Sanje Qi
10 min readAug 18, 2019

--

What are Props in React?

Normally you start out with React’s JSX syntax for rendering something to the browser when learning about React. Basically, JSX mixes HTML with JavaScript to get the best of both worlds.

import React, { Component } from 'react';class App extends Component {
render() {
const greeting = 'Welcome to React';
return (
<div>
<h1>{greeting}</h1>
</div>
);
}
}
export default App;

Pretty soon you will split out your first React component.

import React, { Component } from 'react';class App extends Component {
render() {
return (
<div>
<Greeting />
</div>
);
}
}
class Greeting extends Component {
render() {
const greeting = 'Welcome to React';
return <h1>{greeting}</h1>;
}
}
export default App;

A common question followed by this act: how to pass the data as params (parameters) from one React component to another component? That’s because you don’t want to have a component rendering static data, but pass dynamic data to your component instead. That’s where React’s props come into play. You can pass data in React by defining custom HTML attributes to which you assign your data with JSX syntax. So don’t forget the curly braces.

import React, { Component } from 'react';class App extends Component {
render() {
const greeting = 'Welcome to React';
return (
<div>
<Greeting greeting={greeting} />
</div>
);
}
}
class Greeting extends Component {
render() {
return <h1>{this.props.greeting}</h1>;
}
}
export default App;

As you can see, the props are received in React’s class component via the this instance of the class. A common question which comes up then: Why aren’t the props received in the render methods signature? It would be similar to functional stateless components then. As for now, the team behind React considered it but didn’t change the API for React class components yet. Maybe it will be changed at some point.

In a functional stateless component, the props are received in the function signature as arguments:

import React, { Component } from 'react';class App extends Component {
render() {
const greeting = 'Welcome to React';
return (
<div>
<Greeting greeting={greeting} />
</div>
);
}
}
const Greeting = props => <h1>{props.greeting}</h1>;export default App;

Since you will find always the props in the function signature, which most of the time is only the container object of your data, but not the data to be used, you can destructure the props early in the function signature. One would call it React props destructuring:

const Greeting = ({ greeting }) => <h1>{greeting}</h1>;

As you have seen, props enable you to pass variables from one to another component down the component tree. In the previous example, it was only a string variable. But props can be anything from integers over objects to arrays. Even React components, but you will learn about this later. You can also define the props inline. In case of strings, you can pass props inside double quotes (or single quotes) too.

import React, { Component } from 'react';class App extends Component {
render() {
return (
<div>
<Greeting greeting="Welcome to React" />
</div>
);
}
}
const Greeting = ({ greeting }) => <h1>{greeting}</h1>;export default App;

But you can also pass other data structures with inline props. In case of objects, it can be confusing for React beginners, because you have two curly braces: one for the JSX and one for the object. That’s especially confusing when passing a style object to a style attribute in React the first time.

import React, { Component } from 'react';class App extends Component {
render() {
return (
<div>
<Greeting greeting={{ text: 'Welcome to React' }} />
</div>
);
}
}
const Greeting = ({ greeting }) => <h1>{greeting.text}</h1>;export default App;

Note: It is important to note that is could lead to performance issues because every time the component renders a new object is created again. But it can be a premature optimization as well when learning only about React.

Basically, that’s how props are passed to React components. As you may have noticed, props are only passed from top to bottom in React’s component tree. There is no way to pass props up to a parent component. We will revisit this issue later in this article. In addition, it’s important to know that React’s props are read-only. There is no way in React to set props (even though it was possible in the past). After all, props are only used to pass data from one component to another component React, but only from parent to child components down the component tree.

React Props vs. State

Passing only props from component to component doesn’t make the component interactive, because nothing is there to change the props. Props are read-only. That’s the time when React state comes into play which can be changed. The state is co-located to a React component.

import React, { Component } from 'react';class App extends Component {
constructor(props) {
super(props);
this.state = {
isShow: true,
};
}
toggleShow = () => {
this.setState(state => ({ isShow: !state.isShow }));
};
render() {
return (
<div>
{this.state.isShow ? <Greeting greeting={greeting} /> : null}
<button onClick={this.toggleShow} type="button">
Toggle Show
</button>
</div>
);
}
}
const Greeting = ({ greeting }) => <h1>{greeting}</h1>;export default App;

In this case, the code uses a ternary operator to either show the greeting or not. You can read up this tutorial about all the conditional renderings in React. The state makes the React components interactive. You can read and write state, whereas props are read-only. Once the state changes, the component renders again. In addition, state can be passed as props to child components too.

import React, { Component } from 'react';class App extends Component {
constructor(props) {
super(props);
this.state = {
isShow: true,
};
}
toggleShow = () => {
this.setState(state => ({ isShow: !state.isShow }));
};
render() {
const greeting = 'Welcome to React';
return (
<div>
<Greeting greeting={greeting} isShow={this.state.isShow} />
<button onClick={this.toggleShow} type="button">
Toggle Show
</button>
</div>
);
}
}
const Greeting = ({ greeting, isShow }) =>
isShow ? <h1>{greeting}</h1> : null;
export default App;

The child component doesn’t know whether the incoming props are state or props from the parent component. The component just consumes the data as props. And the child component re-renders too once the incoming props changed.

In conclusion, every time the props or state change, the rendering mechanism of the affected component is triggered. That’s how the whole component tree becomes interactive, because after all, state is passed as props to other components, and once the state in a component changes, which may be passed as props to the child components, all affected components render again.

How to pass Props from child to parent Component?

This a common question for React beginners and the answer for it is brief: there is no way to pass props from a child to a parent component. Let’s revisit the previous example, but this time with an additional Button component for the toggle mechanism.

import React, { Component } from 'react';class App extends Component {
render() {
const greeting = 'Welcome to React';
return (
<div>
{isShow ? <Greeting greeting={greeting} /> : null}
<Button />
</div>
);
}
}
class Button extends Component {
constructor(props) {
super(props);
this.state = {
isShow: true,
};
}
toggleShow = () => {
this.setState(state => ({ isShow: !state.isShow }));
};
render() {
return (
<button onClick={this.toggleShow} type="button">
Toggle Show
</button>
);
}
}
const Greeting = ({ greeting }) => <h1>{greeting}</h1>;export default App;

So far, the Button component manages its own co-located state. Since the Button component manages the isShow property, there is no way to pass it up as props to the App component. The App component needs the isShow property though for the conditional rendering of the Greeting component. At the moment, the application wouldn’t work this way. That’s the point when you have to lift state up for making it accessible for other components (in this case the App component itself) as state (or as passed props for other components).

import React, { Component } from 'react';class App extends Component {
constructor(props) {
super(props);
this.state = {
isShow: true,
};
}
toggleShow = () => {
this.setState(state => ({ isShow: !state.isShow }));
};
render() {
const greeting = 'Welcome to React';
return (
<div>
{this.state.isShow ? <Greeting greeting={greeting} /> : null}
<Button onClick={this.toggleShow} />
</div>
);
}
}
const Button = ({ onClick }) => (
<button onClick={onClick} type="button">
Toggle Show
</button>
);
const Greeting = ({ greeting }) => <h1>{greeting}</h1>;export default App;

The important ingredient is that the App component passes down a function in the props to the Button component now. The function is used for the click handler in the Button component. However, the Button doesn’t know the business logic of the function, only that it has to trigger the function when the button gets clicked. Above in the App component, the state is changed when the passed function is called, and thus all affected components, which use the changed state or consume it as props, render again. Now you can even pass the state as props to the Greeting component.

import React, { Component } from 'react';class App extends Component {
constructor(props) {
super(props);
this.state = {
isShow: true,
};
}
toggleShow = () => {
this.setState(state => ({ isShow: !state.isShow }));
};
render() {
const greeting = 'Welcome to React';
return (
<div>
<Greeting greeting={greeting} isShow={this.state.isShow} />
<Button onClick={this.toggleShow} />
</div>
);
}
}
const Button = ({ onClick }) => (
<button onClick={onClick} type="button">
Toggle Show
</button>
);
const Greeting = ({ greeting, isShow }) =>
isShow ? <h1>{greeting}</h1> : null;
export default App;

As said, there is no way passing props from a child to a parent component. But you can always pass functions from parent to child components, whereas the child components make use of these functions and the functions may change the state in a parent component above. Once the state has changed, the state is passed down as props again. All affected components will render again. For instance, the same pattern applies for having page components in your React application. Once you want to pass data from page to another in React, you can lift the state up to the component (usually App component) which has all page components as its child components. Then the data is managed as state in the top level component but still can be distributed to all child components.

React …props syntax

Another strategy for passing all props to a child component is the JavaScript spread operator. JavaScript’s spread operator in React is a useful power feature and you can read people referring to it as the React …props syntax even though it is not really a React feature but just a thing coming from JavaScript.

class App extends Component {
render() {
const greeting = {
subject: 'React',
description: 'Your component library for ...',
};
return (
<div>
<Greeting {...greeting} />
</div>
);
}
}
const Greeting = ({ subject, description }) => (
<div>
<Title title={`Welcome to ${subject}`} />
<Description description={description} />
</div>
);
const Title = ({ title }) => <h1>{title}</h1>;const Description = ({ description }) => <p>{description}</p>;

The props spreading can be used to spread a whole object with key value pairs down to a child component. It has the same effect as passing each value of the object by its own to the component as before. The child component gets the props the same way as before too. Another thing which builds up on top of the prop spread is the prop spread with rest.

class App extends Component {
render() {
const greeting = {
subject: 'React',
description: 'Your component library for ...',
};
return (
<div>
<Greeting {...greeting} />
</div>
);
}
}
const Greeting = ({ subject, ...other }) => (
<div>
<Title title={`Welcome to ${subject}`} />
<Description {...other} />
</div>
);
const Title = ({ title }) => <h1>{title}</h1>;const Description = ({ description }) => <p>{description}</p>;

As you can see, in the Greeting component the props are destructured but with a rest assignment which is called other in this case. So you have the subject prop but also the other prop which is essentially just an object with all the remaining properties (in this case only the description). Now you can spread the rest of the props to the Description component, because all the relevant props for the other components (here the Title component) were separated from it.

React props with default value

In some cases you may want to pass default values as props. Historically the best approach to it was using JavaScript’s logical OR operator.

const Greeting = ({ subject, description }) => {
subject = subject || 'Earth';
return (
<div>
<Title title={`Welcome to ${subject}`} />
<Description description={description} />
</div>
);
};

You can also inline it as prop:

const Greeting = ({ subject, description }) => (
<div>
<Title title={`Welcome to ${subject || 'Earth'}`} />
<Description description={description} />
</div>
);

However, with JavaScript language additions there are other features you can use for it. For instance JavaScript’s default parameter for the default value of the prop:

const Greeting = ({ subject = 'Earth', description }) => (
<div>
<Title title={`Welcome to ${subject}`} />
<Description description={description} />
</div>
);

The latter is the most popular choice when using only JavaScript for the default value. However, in case you are using React’s PropTypes, it’s is also possible to pass default prop values the React way:

const Greeting = ({ subject, description }) => (
<div>
<Title title={`Welcome to ${subject}`} />
<Description description={description} />
</div>
);
Greeting.defaultProps = {
subject: 'Earth',
};

After all, my recommendation would be using JavaScript’s default parameter, because everyone outside of the React world understands it as well. Moreover, often you will not have React’s PropTypes in your project for making use of the PropTypes default in the first place.

React’s children prop

The children prop in React can be used for composing React components into each other. Rather than using inheritance (due to the nature of React’s class components), React embraces composition over inheritance. That’s why you can just put any string, element(s) or React component(s) in between of the opening and closing component tags.

class App extends Component {
...
render() {
const greeting = 'Welcome to React';
return (
<div>
<Greeting greeting={greeting} isShow={this.state.isShow} />
<Button onClick={this.toggleShow}>Toggle Show</Button>
</div>
);
}
}
const Button = ({ onClick, children }) => (
<button onClick={onClick} type="button">
{children}
</button>
);

In this case, only a string is put in between of the component tags. Then in the child component, you can make use of everything which is in between of the tags (string, element(s), component(s)), by using React’s children prop. For instance, you can just render the content of the children prop like it is done in this example. More to follow on this topic.

Source: StackOverflow, Robin Wieruch, Medium, ReactJs, MDN

--

--