React Context

How and when can you use it ?

http://pierr.github.io/react-context-example

Pierre Besson

KleeGroup

  • Application métier
  • UX
  • 3 years of big JavaScript projects
  • 15+ Projets avec React

React Basics

A component can be written in several ways

A component has props and can have a state

ES5

                        
var MyOldWayComponent = React.createClass({
  displayName: 'MyOldWayComponent',
  render: function renderOldWayComponent(){
    return 
MyOldWayComponent
; } });

ES2015 Class

                        
class MyClassComponent extends React.Component {
  render(){
    const {state, props} = this;
    return (
        
  • Props :{JSON.stringify(props)}
  • State: {JSON.stringify(state)}
); } }

Pure function

                        
function MyFunctionComponent(props) {
    return 
Props: {JSON.stringify(props)}
}

The context in React

https://facebook.github.io/react/docs/context.html

Components have props,state and also context

  • The main purpose of the context is to pass information to a React sub tree.
  • It is Dependency Injection
  • Officialy documented in React since last September
  • Not to be considered stable

Parent provides the context

  • A getChildContext method must be implemented
  • A childContextTypes object must be set
						
class MyAwesomeRootComponent extends React.Component {
  render(){
    return (
        
Component Awesome <Child1/> {this.props.children}
); } getChildContext(){ return {color: "purple"}; } }
MyAwesomeRootComponent.childContextTypes = { color: React.PropTypes.string }

Child, GrandChild use the context

  • A context object is in the component's this
  • A childContextTypes object must be set

Child using Context

						
class ComponentUsingContext extends React.Component {
	render(){
		const {color} = this.context;
		return 
{color}
; } }
ComponentUsingContext.contextTypes = { color: React.PropTypes.string }

Child using Context

						
function MyFuncComponent(props, context){
		const {color} = this.context;
		return 
{color}
; } }
MyFuncComponent.contextTypes = { color: React.PropTypes.string }

In action

						
function App(props){
    return (
        <MyAwesomeRootComponent>
          App
          <Child1 />
          <Child2 />
          <Child3 />
         </MyAwesomeRootComponent>
     );
  }

  render(<App/>, document.querySelector("div#root"));
						
					

Super Easy...

It also raises questions

  • Why would I need to to so instead of passing direct props ?
  • Why isn't it more used by React community ?
  • Who use it?
  • Which libs use it? ?

Use case

A minimal component tree

						
class Parent extends React.Component {
    render(){
        const {lang} = this.props;
        return (
                
<Child lang={lang}>
); } }
function Child({lang}){ return (
<GrandChild lang={lang}>
); }
function GrandChild({lang}){ return (
{lang}
); }

Render in the DOM

                    
ReactDOM.render(<Parent lang='fr'/>, document.querySelector('div#app'));
						
					
JSBIN

What to say

  • Explicit lang props is really understandable
  • The Child only transfers the lang to the GrandChild without needing it
  • The components are not pure regarding `lang`
  • If the lang feature is updated, you may need to change all your props

What can we say about the Tree

  • pass `props` through a React Component tree is sometimes a pain and inBetween components needs to transfer props without needing them
  • You need reusable components
  • You need behaviour
  • props transfer is hard to maintain due to coupling

This problem is even more a pain when you need to abstract things in your components.

Why don't we use the Context ?

A minimal component tree

						
class Parent extends React.Component {
    getChildContext(){
        return {this.props.lang};
    }
    render(){
        return (
                

Parent

<Child>
); } } Parent.childContextTypes = { lang: React.PropTypes.string };


function Child(props){
    return (

Child

<GrandChild>
); }
function GrandChild(props, {lang}){ return (

Child

{lang}
); }
GrandChild.contextTypes = { lang: PropTypes.string } ReactDOM.render(<Parent lang='fr'/>, document.querySelector('div#app'))

What can we say about that?

  • The parent does not have to pass direct props to the child
  • The context is protected with childContextTypes and contextType
  • code is quite clear
  • There is a not so explicit coupling between
  • The childContextTypes is a pain to write on each time
  • It is not readable

Can we find a better way to use the context ?

Can we be inspired by something great?

React Redux bindings

  • State is process with redux using reducer
  • The binding between react and redux is done using context
  • In react-redux there is a simple pattern which is Connect and Provider object

Simple redux example

						
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './containers/App'
import todoApp from './reducers'
let store = createStore(todoApp);
let rootElement = document.getElementById('root')
const ConnectedApp = connect(getState)(App)

render(
  <Provider store={store}>
    <ConnectedApp />
  </Provider>,
  rootElement
)
						
					

Can we apply this pattern to our previous example?

We need to introduce

a LangProvider and LangConnector

LangProvider and LangConnector

                        
class Parent extends React.Component {
    render(){
        return (
                

Parent

<Child/>
); } }
                
function Child({lang}){
    return (

Child

<GrandChild />
); }
function GrandChild({lang}){ return (

GrandChild

{lang}
); }
connectLang(GrandChild); ReactDOM.render(<LangProvider lang='fr'><Parent/></LangProvider>, document.querySelector('div#app'))

So what we have now ?

  • Our components are now pure
  • Put an object in the context is now explicit with the Provider
  • Using an object from the context is explicit
  • Components do not depend from context, they are pure
  • No context type validation rewritten several times

How can we create the LangProvider?

                        
class LangProvider extends React.Component {
  getChildContext(){
    return {lang: this.props.lang};
  }
  render() {
    return this.props.children;
  }
}
                        
                        
Provider.childContextTypes = {
  lang: React.PropTypes.string
};
                        
                    

How can we create the LangConnector?

We will do a Higher Order Component in a decorator

                        
function connectLang(ComponentToConnect){

    class ConnectedOnLang extends React.Component {
        render() {
            const {lang} = this.context;
            return <ComponentToConnect lang={lang} {...this.props} /> ;
        }
    }

    ConnectedOnLang.displayName = `${ComponentToConnect.displayName}ConnectedOnLang`;

    ConnectedOnLang.contextTypes = {
            lang: React.PropTypes.string.isRequired
    };

    return ConnectedOnLang;
}
                        
                    

Quite easy to write and to separate code

  • Separation of concerns between components
  • We can use pure function components with only props

API and lifecycle

  • void componentWillReceiveProps(object nextProps, object nextContext)
  • boolean shouldComponentUpdate(object nextProps, object nextState, object nextContext)
  • void componentWillUpdate(object nextProps, object nextState, object nextContext)
  • void componentDidUpdate(object prevProps, object prevState, object prevContext)

Real life use case

Application's state with REDUX

Route with React router

RouteComponent, RouteComponent

Recompose

React utility belt for function components and higher-order components

Metadata on Fields

                        
{
    movie: {
        code: {
            domain: 'DO_ID',
            required: true
        },
        title: {
            domain: 'DO_LABEL_LONG',
            required: true
        },
        originalTitle: {
            domain: 'DO_LABEL_LONG',
            required: false
        }
    }
}
                        
                    

Master data

Complex mixins

When you cannot convert your mixin in an Higher Order Component

Conclusion

React doc warns us that is is not a stable API

Prefer direct props when you can

When should you use the context ?

When you want to abstract something
For things such as current user, language, theme, metadata.
Every where you would have use a global variable or a separate module.
When you wan to abstract a behaviour (often use to be complex mixins).

Informations