Context matters

by elizaveta anatskaya

Prop drilling problem

As the data flow is one direction in React. When any data is required at nth level, the data will have to be passed as a prop through all the child components.

Renaming props halfway through

Over-forwarding props

Refactor the shape of some data

How to avoid problems with prop drilling?

  • Use state management libraries like Redux and MobX
  • Use compoment composition
  • Keep state as close to where it's relevant as possible
  • Use React's Context API for things that are truly necessary deep in the react tree.

 

What is Context API?

Context API is a way to enable components to share some data without explicitly passing via each component manually.
 

Context is like a global object to the React component sub-tree.

What are the use cases to use Context API?

  1. Themes
  2. Multilingual application
  3. Authorisation: setting the user role and info
    ...

Context should be used only for the data, which will be used at various levels in the React component tree.

How to create Context?

 

For better maintainability React allows us to create multiple contexts in one app.

export const CountryContext = createContext({});
export const LanguageContext = createContext('en');
export const CountryContext = React.createContext({});
export const LanguageContext = React.createContext('en');
import { createContext } from 'react' ;
export const ThemeContext = createContext('light');
import React from 'react';
export const ThemeContext = React.createContext('light');

createContext returns a pair of Provider and Consumer components and takes on defaultValue of the current context

const { Provider, Consumer} = ThemeContext;
ThemeContext.Provider;
ThemeContext.Consumer;

provider

– is a component that enables Consumers to subscribe to the context changes. The provider accepts a value prop and the data in this prop is available to all the child consumers.

class App extends Component {

 const ThemeContext = createContext('defaultValue');
//  const {Provider} = createContext('defaultValue');


  render() {
    return (
      <ThemeContext.Provider value={'light'}}> 
        <Header toggleTheme={this.toggleTheme}/>
        <Body/>
      </ThemeContext.Provider>
//  	  <Provider value={'light'}}> 
//         <Header toggleTheme={this.toggleTheme}/>
//         <Body/>
//       </Provider>
    );
  }
}

export default App;

Consumer

— is a getter of context. The components can subscribe to context values by wrapping the Consumer around it

import React, { Component } from 'react'

export default class Body extends Component {
  
  const ThemeContext = React.createContext('defaultValue');
//   const { Consumer } = createContext('defaultValue');

  render() {
    return (
      <ThemeContext.Consumer>
        {
          (theme) => (
            <div 
      		className="body" 
      		style={{color: theme.fontColor}}>
             Some text goes here
            </div>
          )
        }
      </ThemeContext.Consumer>
//       <Consumer>
//         {}
//       </Consumer>
    )
  }
}
import React, { Component } from 'react'

export default class Body extends Component {
  
  const ThemeContext = React.createContext('defaultValue');   
       
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    return (
      <div 
      className="body" 
      style={{color: theme.fontColor}}
      >
        Some text goes here
      </div>
  )
  }
}

Consumer

— is a getter of context. The components can subscribe to context values by wrapping the Consumer around it

import React, { Component } from 'react'

export default class Body extends Component {
  
  const ThemeContext = React.createContext('defaultValue');
//   const { Consumer } = createContext('defaultValue');

  render() {
    return (
      <ThemeContext.Consumer>
        {
          (theme) => (
            <div 
      		className="body" 
      		style={{color: theme.fontColor}}>
             Some text goes here
            </div>
          )
        }
      </ThemeContext.Consumer>
//       <Consumer>
//         {}
//       </Consumer>
    )
  }
}

use context

const value = useContext(MyContext);
Correct: useContext(MyContext)
Incorrect: useContext(MyContext.Consumer)
Incorrect: useContext(MyContext.Provider)

don’t forget that the argument to useContext must be the context object itself:

 

import React from 'react';
import ThemeContext from './ThemeContext';
 
const C = () => {
  const {color} = React.useContext(ThemeContext);
 
  return (
    <p style={{ color }}>
      Hello World
    </p>
  );
};

Accepts a context object (the value returned from React.createContext) and returns the current context value for that context.

React Context will trigger a re-render on each update, and optimizing it manually can be really tough

Cool, but keep in mind

With the useContext API and React Hooks, there is no need to install external libraries

What if we use hoc?

Every time the HOC is created, the component instance starts with a fresh state, and so the buttons’ themes are independent of each other.

thanks! 👏

Context

By Elizabeth Anatskaya