React 16 Upgrade

New Lifecycle

Mounting

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()
  • UNSAFE_componentWillMount()

Updating

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()
  • UNSAFE_componentWillReceiveProps()
  • UNSAFE_componentWillUpdate()

Previously, it was only called if the component was re-rendered by its parent,
and would not fire as the result of a local setState.

// Ok in react 16.3 but Bug in react16.4static getDerivedStateFromProps(props, state) {  if (props.value !== state.controlledValue) {    return {      // Since this method fires on both props and state changes, local updates      // to the controlled value will be ignored, because the props version      // always overrides it. Oops!      controlledValue: props.value,    };  }  return null;}

// Fixed in react16.4static getDerivedStateFromProps(props, state) {  const prevProps = state.prevProps;  // Compare the incoming prop to previous prop  const controlledValue =    prevProps.value !== props.value      ? props.value      : state.controlledValue;  return {    // Store the previous props in state    prevProps: props,    controlledValue,  };}

getSnapshotBeforeUpdate(prevProps, prevState) {  if (prevProps.list.length < this.props.list.length) {    const list = this.listRef.current;    return list.scrollHeight - list.scrollTop;  }  return null;}componentDidUpdate(prevProps, prevState, snapshot) {  if (snapshot !== null) {    const list = this.listRef.current;    list.scrollTop = list.scrollHeight - snapshot;  }}

Unmounting

  • componentWillUnmount

Error Handling

  • componentDidCatch

class ErrorBoundary extends React.Component {    constructor(props) {        super(props);        this.state = { hasError: false };    }    componentDidCatch(error, info) {        // Display fallback UI        this.setState({ hasError: true });        logErrorToMyService(error, info);    }    render() {        if (this.state.hasError) {            return <h1>Something went wrong.</h1>;        }        return this.props.children;    }}

Then you can use it as a regular component:

<ErrorBoundary>  <MyWidget /></ErrorBoundary>

Context API

API

  • React.createContext()
  • Provider
  • Consumer

// AppContext.jsexport const AppContext = React.createContext({  count: 0});const withApp = (Component) => (props) => (  <Consumer>    {value => <Component {...props} count={value.count}/>}  </Consumer>);// CountButton.jsconst {Consumer} = AppContext;// @withApp Decorator in ES2016class CountButton extends Component {  render() {    return(      <button type="button" {...this.props}>{this.props.count}</button>    );  }});export default = withApp(CountButton);// App.jsconst {Provider} = AppContext;class App extends Component {  constructor(props) {    super(props);    this.state = {      count: 0    };  }  add = () => {    this.setState({      count: this.state.count + 1    });  };  render() {    return (      <div>        <Provider value={this.state}>          <CountButton onClick={this.add}/>        </Provider>        <CountButton onClick={this.add}/>      </div>    );  }}

New Render Types

//stringrender(){  return 'hello,world'} //numberrender(){  return 12345} //booleanrender(){  return isTrue?true:false} //nullrender(){  return null} //arrays ,need key . (fragments)render(){  return [    <div>hello</div>,    <span>world</span>,    <p>oh</p>  ]}//portals...

Fragments

A common pattern in React is for a component to return multiple elements.
Fragments let you group a list of children without adding extra nodes to the DOM.

render() {  return (    <React.Fragment>      <ChildA />      <ChildB />      <ChildC />    </React.Fragment>  );}

Portals

// React mounts a new div and renders the children into itreturn (  <div>    {this.props.children}  </div>);

const modalRoot = document.getElementById('modal-root');// Modal.jsconstructor(props) {  super(props);  this.el = document.createElement('div');}componentDidMount() {  modalRoot.appendChild(this.el);}componentWillUnmount() {  modalRoot.removeChild(this.el);}render() {  // React does *not* create a new div. It renders the children into `domNode`.  // `domNode` is any valid DOM node, regardless of its location in the DOM.  return ReactDOM.createPortal(    this.props.children,    this.el  );}

CreateRef & ForwardRef

const FancyButton = React.forwardRef((props, ref) => (  <button ref={ref}           className="FancyButton"           onClick={props.onClick}>    {props.children}  </button>));// You can now get a ref directly to the DOM button:class MyComponent extends React.Component {  constructor(props) {    super(props);    this.myRef = React.createRef();  }  handleClick = () => {    // ref.current -> dom    this.myRef.current.focus();  };  render() {    return (      <FancyButton ref={this.myRef}                    onClick={() => console.log(this.myRef.current)}>        Click me!      </FancyButton>    );  }}// <button class="FancyButton">Click me!</button>

SetState()

  • Calling setState with null no longer triggers an update.
  • Calling setState directly in render always causes an update.
  • setState callbacks (second argument) now fire immediately after componentDidMount / componentDidUpdate instead of after all components have rendered.

DOM Attributes

In the past, React used to ignore unknown DOM attributes.
Now, any unknown attributes will end up in the DOM.

// Your code:<div mycustomattribute="something" />// React 15 output:<div />// React 16 output:<div mycustomattribute="something" />

You should still use the canonical React naming for known attributes.

// Yes, please<div tabIndex="-1" />// Warning: Invalid DOM property `tabindex`. Did you mean `tabIndex`?<div tabindex="-1" />

Pointer Events (React 16.4)

The following event types are now available in React DOM:

  • onPointerDown
  • onPointerMove
  • onPointerUp
  • ……

These events will only work in browsers that support the Pointer Events specification.