web-dev-qa-db-fra.com

Quelle est la méthode correcte du cycle de vie dans React 16.3 pour mettre à jour la toile à partir des accessoires

J'ai un composant Canvas, qui ressemble à peu près à ceci:

class Canvas extends React.Component{

    saveRef = node => {
        this._canvas = node;
    }
    
    shouldComponentUpdate(){
        /*I will never re-render this component*/
        return false;
    }
    
    componentWillReceiveProps( nextProps ){
        /*Here I do manipulations with this._ctx, when new props come*/
    }
    
    render(){
        return (
            <canvas ref={this.saveRef} />
        );
    }
    
    componentDidMount(){
        this._ctx = this._canvas.getContext( "2d" );
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

La communauté de réaction a commencé à déprécier componentWillReceiveProps afin de le remplacer par getDerivedStateFromProps. Je peux utiliser componentDidUpdate pour réaliser mes dessins, mais je dois ensuite supprimer shouldComponentUpdate et j'aurai beaucoup d'appels de rendu inutiles. Quelle est la méthode la plus performante pour mettre à jour mon composant dans la réaction 16.3, lorsque de nouveaux accessoires arrivent?

11
Alex Fomin

Utilisez componentDidUpdate pour les manipulations DOM comme celle-ci. Un shouldComponentUpdate ne fera pas vraiment la différence pour un composant avec un seul enfant qui a toujours les mêmes accessoires. Vous devriez donc pouvoir l’enlever sans différence significative de performances.

Si vous avez profilé l'application et déterminé que dans ce cas particulier cela fait / fait une différence, vous pouvez hisser l'élément en constructeur.

De cette façon, React l’ignorera complètement (ce qui fonctionne effectivement de la même manière que shouldComponentUpdate):

class Canvas extends React.Component {
  constructor(props) {
    super(props);
    this._ctx = null;
    this._child = <canvas ref={node => {
      this._ctx = node ? node.getContext('2d') : null
    } />;
  }

  componentDidUpdate(prevProps){
    // Manipulate this._ctx here
  }

  render() {
    // A constant element tells React to never re-render
    return this._child;
  }
}

Vous pouvez également le scinder en deux composants:

class Canvas extends React.Component {
  saveContext = ctx => {
    this._ctx = ctx;
  }

  componentDidUpdate(prevProps){
    // Manipulate this._ctx here
  }

  render() {
    return <PureCanvas contextRef={this.saveContext} />;
  }
}


class PureCanvas extends React.Component {
  shouldComponentUpdate() {
    return false;
  }

  render() {
    return (
      <canvas
        ref={node => node ? this.props.contextRef(node.getContext('2d') : null)}
      />;
  }
}
12
Dan Abramov

J'ai trouvé cela parce que j'avais une sorte de problème similaire mais pas complètement identique. La solution qui fonctionne pour moi consiste simplement à mettre tout le code pertinent dans shouldComponentUpdate:

(l'instruction if était auparavant dans componentWillReceiveProps)

  shouldComponentUpdate (nextProps, nextState) { // no more random renders
    if (
      (nextProps.nightMode !== this.props.nightMode) ||
      (nextProps.language  !== this.props.language)
    ) {
      this.props.setRefresh(true)                       // setTimeout means after current operation
      setTimeout(() => this.props.setRefresh(false), 1) // so loading will show for longer than 1ms
    }

    return this.props.refresh !== nextProps.refresh
  }
0
ThaJay