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

Sanje Qi
5 min readSep 1, 2019

--

part1 & part2

REACT’S CONTEXT API

At some point, you are passing a lot of props down your component tree. Depending on the depth of the component tree, it can happen that many props are passed from a top level component to all the leaf components. Every component in between has to pass the props even though it may not be interested in the props. The problem is called prop drilling in React. There are a couple of solutions to overcome this “problem”. You have already learned about one solution: passing components as props by using the slot pattern. Then you don’t have to pass a prop through all components, but rather distribute the props at a top level to all slotted components.

Another solution is React’s Context API which can be used to pass props implicitly down to component tree. Every component which is interested in the props passed by React’s Context API can consume them. All the other components don’t need to consume them and thus they will never know about the props. Moreover, the components between the top level and the leaf components don’t need to know about the props as well. Checkout React’s Context API if you are interested in using it.

HOW TO SET PROPS TO STATE?

Previously you have got to know more about props and state in React. Sometimes there is one question which comes up for React beginners, but also for experienced React developers when implementing React components: How to set props to state? In case of the initial state, it is totally fine to derive it from the props. You can do it in the constructor of a class component:

class Profile extends Component {constructor(props) {super(props);this.state = {value: props.initialValue,};}onChange = event => {this.setState({ value: event.target.value });}render() {return (<label>Name:<inputvalue={this.state.value}onChange={this.onChange}type="text"/></label>);}}

That’s a common pattern in React. But what about incoming props which are changing and should be set to the state then? You can do it by using the getDerivedStateFromProps(props, state) lifecycle method. It is invoked before the render lifecycle method for the mounting and update lifecycles of the component. By using this lifecycle method, it is possible to set the state of the component based on changing props.

However, the getDerivedStateFromProps() lifecycle method exists only for rare use cases. Usually one should think twice about the implementation logic of the component(s) before using this lifecycle method. This article doesn't go into detail about this topic, because there already exists an excellent article about it: You Probably Don't Need Derived State. You should definitely check it out in case you are running into an edge case where derived state is applicable. Usually there is a way around this lifecycle method and the article explains these scenarios in great detail.

REACT PROPS PITFALLS

There are a couple of pitfalls when passing props in React. Here I want to collect a list of these things in case someone stumbles upon them:

React props are not being passed in Components

Sometimes you run into the problem that your React props are not being passed. Personally I ran into this issue several times when I forgot to use the curly braces for the props destructuring in my functional stateless components:

const Button = (onClick, children) => (<button onClick={onClick} type="button">{children}</button>);

In this case, the onClick argument is the actual props. So you have to destructure it, so the first argument of the function, for accessing the onClick and children props.

const Button = ({ onClick, children }) => (<button onClick={onClick} type="button">{children}</button>);

Most often that’s already the solution to the problem. If not, you should track down the prop from parent to child component by adding console.log(props) calls to your components. If you have a functional stateless component, you don't necessarily need to add an arrow function body to your component for putting the console.log() in between. Instead you can use this neat trick:

const Button = ({ onClick, children }) =>console.log(onClick, children) || (<button onClick={onClick} type="button">{children}</button>);

The console.log() always evaluates to undefined (a falsey value) and thus the component is still being rendered. That's how you can easily get a logging for your props in between your input and output of your functional stateless component.

React props key is undefined

When rendering lists in React, you have to use a key prop identifying the element in the list. React uses the key for performance reasons, but also for keeping track of the element in case your list changes (e.g. due ordering, removing, adding of items). That’s why you should use a unique identifier which is associated to the rendered item.

const List = ({ users }) => (<ul>{users.map(user => <Item key={user.id}>{user.name}</Item>)}</ul>);const Item = ({ children }) => (<p>{children}</p>);

So far, everything is alright with this code. However, sometimes you want to get the key prop in the child component.

const List = ({ users }) => (<ul>{users.map(user => <Item key={user.id}>{user.name}</Item>)}</ul>);const Item = ({ key, children }) => (<p>{key} {children}</p>);

That’s not working and you will also see a warning in your developer console log: “… key is not a prop. Trying to access it will result in undefined being returned. In this case, you have to pass a second prop when you want to get the key from the props.

const List = ({ users }) => (<ul>{users.map(user => (<Item key={user.id} id={user.id}>{user.name}</Item>))}</ul>);const Item = ({ id, children }) => (<p>{id} {children}</p>);

In the end, that’s the workaround to pass props (e.g. key) which are internally used by React and not passed to the child components.

Pass props to Styled Components

Did you hear about styled components? They can be used for styling your components in React. Rather than thinking about cascading style sheets as for HTML styles, you only style your components. So the style becomes more co-located to your components. In fact, in the case of styled components, the style becomes a React component:

import React, { Component } from 'react';import styled from 'styled-components';const Input = styled.input`padding: 0.5em;margin: 0.5em;color: palevioletred;background: papayawhip;border: none;border-radius: 3px;`;class App extends Component {constructor(props) {super(props);this.state = {value: '',};}onChange = event => {this.setState({ value: event.target.value });}render() {return (<div><Inputvalue={this.state.value}onChange={this.onChange}/></div>);}}

The input element which is used to implement the Input styled component gets the value and onChange as props automatically. But what if you want to get props in a styled component to do something with them? Then you can add a string interpolation in the template literal and get the props in the inlined function's signature:

import React, { Component } from 'react';import styled from 'styled-components';const Input = styled.input`padding: 0.5em;margin: 0.5em;color: palevioletred;background: papayawhip;border: none;border-radius: ${props => props.hasRadius ? '3px' : '0px'};`;class App extends Component {...render() {return (<div><Inputvalue={this.state.value}onChange={this.onChange}hasRadius={true}/></div>);}}

Basically that’s how you pass props to styled components and how you get props in a styled component. If you haven’t used styled components yet, you should give them a shot for styling your React components without thinking about CSS files.More to follow on this topic.

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

--

--