React Context – How and when can you use it ? – Pierre Besson



React Context – How and when can you use it ? – Pierre Besson

0 0


react-context-example

react-context-example

On Github pierr / react-context-example

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 <div>MyOldWayComponent</div>;
  }
});
                        
                    

ES2015 Class

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

Pure function

                        
function MyFunctionComponent(props) {
    return <div>Props: {JSON.stringify(props)}</div>
}
                        
                    

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 (
        <div>
            <span>Component Awesome</span>
            <Child1/>
            {this.props.children}
        </div>
    );
  }
  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 <div>{color}</div>;
	}
}
                        
                        
ComponentUsingContext.contextTypes = {
   color: React.PropTypes.string
 }
						
					

Child using Context

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

In action

						
function App(props){
    return (
        <MyAwesomeRootComponent>
          <span>App</span>
          <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 (
                <div><Child lang={lang}></div>
            );
    }
}
                    
                    
function Child({lang}){
    return (<div><GrandChild lang={lang}></div>);
}
                    
                    
function GrandChild({lang}){
    return (<div>{lang}</div>);
}
                    
                

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 (
                <div><h1>Parent</h1><Child></div>
            );
    }
}
Parent.childContextTypes = {
    lang: React.PropTypes.string
};


function Child(props){
    return (<div><h2>Child</h2><GrandChild></div>);
}


function GrandChild(props, {lang}){
    return (<div><h3>Child</h3>{lang}</div>);
}


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 (
                <div><h1>Parent</h1><Child/></div>
            );
    }
}
                
            
                
function Child({lang}){
    return (<div><h2>Child</h2><GrandChild /></div>);
}
                
                
function GrandChild({lang}){
    return (<div><h3>GrandChild</h3>{lang}</div>);
}
            
            
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

React Context How and when can you use it ? http://pierr.github.io/react-context-example