Understanding the New Context API

Speaker

@emaSuriano

http://emanuelsuriano.design/

#100DaysOfCode

Gold Sponsor

What the f**k is Context? 🤔

What is?

Context is another way of sharing information between parents and children (like props).

 Using Props is an Explicit Communication.

While Context is an Implicit Communication.

// Via Props

<Parent>   props: {  value: 1 }  ↓
  <Intermediate>   props: {  value: 1 }  ↓
    <OtherIntermediate>    props: {  value: 1 }  ↓
      <Child>    props: {  value: 1 }



// Via Context 

<Parent>   context: {  value: 1 }
  <Intermediate>
    <OtherIntermediate>
      <Child>    props: {  value: 1 }

Props

import React, { Component } from 'react';
import { number } from 'prop-types';

class Parent extends Component {
  render() {
    return <Child value={1}/>;
  }
}

class Child extends Component {
  static propTypes = {
    value: number
  };

  render() {
    const { value } = this.props;
    return (<p>
        The value from Context is: {value}
    </p>);
  }
}

Context

import React, { Component } from 'react';
import { number } from 'prop-types';

class Parent extends Component {
  static childContextTypes = {
    value: number
  };

  getChildContext() {
    return { value: 1 };
  }

  render() {
    return <Child />;
  }
}

class Child extends Component {
  static contextTypes = {
    value: number
  };

  render() {
    const { value } = this.context;
    return (<p>
        The value from Context is: {value}
    </p>);
  }
}

Why will you use it?

When an application starts to grow, is common to start seeing the Prop Drilling problem.

This is having components passing down lots of props just to give access to component below of it 👎

Why did React rewrite Context?

The main flaw with context today is how it interacts with shouldComponentUpdate

😨

What is going on?

shouldComponentUpdate returning false causes any Context update to be no longer propagated to Child Components

What does this new API offer?

Not Ready Yet ✋

Will come in React 16.3.0

List of Changes

  • Remove the need for using getChildContext to set values inside a context.
  • Remove contextType and childContextTypes static definition.
  • Add a new method React.createContext which create a new instance of a Context and return:
    • A Provider component allows you to define values inside the Context.
    • A Consumer component gives access to all the information inside the Context.

Old 👴

import React, { Component } from 'react';
import { number } from 'prop-types';

class Parent extends Component {
  static childContextTypes = {
    value: string
  };

  getChildContext() {
    return { value: 1 };
  }

  render() {
    return <Child />;
  }
}

class Child extends Component {
  static contextTypes = {
    value: number
  };

  render() {
    const { value } = this.context;
    return (<p>
        The value from Context is: {value}
    </p>);
  }
}

New 👶

import React, { createContext } from 'react';
import { number } from 'prop-types';

const { Provider, Consumer } = 
    createContext('contextExample')

const Parent = () => (
  <Provider value={ value: 1}>
    <Child />
  </Provider>
)

const Child = () => (
  <Consumer>
    ({value}) =>
      <p>The value from Context is: {value}</p>
  </Consumer>
)

Let's build something

👷

⚠️ Disclaimer ⚠️ 
No components with today context were wounded while refactoring 

Task: Implement a RadioGroup

Responsibilities:

  • Render multiple radio buttons.
  • Radio buttons are assigned to the same group by a name property.
  • Only one radio button must be selected per group.

Implementing a RadioGroup

Vanilla React

Implementing a RadioGroup

With Context 💪

RadioGroupContext

This is the core communication of the library.

 

Create the context using the new API and return a Provider and Consumer.

//RadioGroupContext.js

import React from 'react';

const { Provider, Consumer } = 
    React.createContext('radioGroup');

export { Provider, Consumer };

RadioGroup

In charge of distributing the information to all the RadioButton:

  • Name of the group for the radio buttons,
  • The callback onChange.
  • The selected radio.
  • If the group is disabled
//RadioGroup.js
import React from 'react';
import { Provider } from './RadioGroupContext';

const RadioGroup =
({ selected, onChange, name, disabled, children }) => (
  <Provider
    value={{
      selected,
      onChange,
      name,
      disabledGroup: disabled,
    }}
  >
    {children}
  </Provider>
);


export default RadioGroup;

RadioButton

Read from the context defined by the RadioGroup and make some assumptions:

  • Selected === id, then checked = true
  • Disabled || disabledGroup, then disabled = true
  • !Value, then value = id
//RadioButton.js
import React from 'react';
import { Consumer } from './RadioGroupContext';

const RadioButton = ({ id, value, disabled, children }) => (
  <Consumer>
    {({ selected, onChange, disabledGroup, name }) => (
      <React.Fragment>
        <input
          type="radio"
          {...selected && { checked: selected === id }}
          disabled={disabled || disabledGroup}
          id={id}
          value={value || id}
          name={name}
          onChange={onChange}
        />
        <label htmlFor={id}>{children}</label>
      </React.Fragment>
    )}
  </Consumer>
);

export default RadioButton;

Great Examples using the New API

react-waterfall

import { initStore } from 'react-waterfall'

const store = {
  initialState: { count: 0 },
  actions: {
    increment: ({ count }) => ({ count: count + 1 }),
  },
}

const { Provider, connect } = initStore(store)

let Count = ({ state, actions }) => (
  <>
    {state.count}
    <button onClick={actions.increment}>+</button>
  </>
)

Count = connect(['count'])(Count)

const App = () => (
  <Provider>
    <Count />
  </Provider>
)

with-context

import { withContext } from "with-context";
import { styles } from "../consts";

export const ThemeContext = React.createContext("light");
export const withTheme = withContext(ThemeContext, "theme");

@withTheme
export default class StatefulComponent extends React.PureComponent {
  render() {
    const { theme } = this.props;
    return (
      <div style={styles[theme]}>StatefulComponent with theme: {theme}</div>
    );
  }
}

const StatelessFunctionalComponent = ({ theme }) => {
  return (
    <div style={styles[theme]}>
      StatelessFunctionalComponent with theme: {theme}
    </div>
  );
};

const StatelessWithTheme = withTheme(StatelessFunctionalComponent);

Last Words

" Think Context as a forbidden top-notch feature for building powerful components in React 🔥🔥🔥

Links

Thanks for listening!

Happy code 💻

Understanding the new Context API

By Emanuel Suriano

Understanding the new Context API

I’m going to tell you my experience of rewriting a Component using this new Context API in React.

  • 289
Loading comments...

More from Emanuel Suriano