web-dev-qa-db-fra.com

Accès au contexte de réaction en dehors de la fonction de rendu

Je développe une nouvelle application en utilisant la nouvelle API de contexte React au lieu de Redux et avant, avec Redux, lorsque j'avais besoin d'obtenir une liste d'utilisateurs, par exemple, j'appelais simplement componentDidMount mon action, mais maintenant, avec React Context, mes actions vivre dans mon consommateur qui est dans ma fonction de rendu, ce qui signifie que chaque fois que ma fonction de rendu est appelée, elle appelle mon action pour obtenir ma liste d'utilisateurs et ce n'est pas bien, car je ferai beaucoup de requêtes inutiles.

Alors, comment puis-je appeler une seule fois mon action, comme dans componentDidMount au lieu d'appeler le rendu?

Juste à titre d'exemple, regardez ce code:

Supposons que j'emballe toute ma Providers dans un composant, comme ceci:

import React from 'react';

import UserProvider from './UserProvider';
import PostProvider from './PostProvider';

export default class Provider extends React.Component {
  render(){
    return(
      <UserProvider>
        <PostProvider>
          {this.props.children}
        </PostProvider>
      </UserProvider>
    )
  }
}

Ensuite, je mets ce composant Fournisseur envelopper toute mon application, comme ceci:

import React from 'react';
import Provider from './providers/Provider';
import { Router } from './Router';

export default class App extends React.Component {
  render() {
    const Component = Router();
    return(
      <Provider>
        <Component />
      </Provider>
    )
  }
}

Maintenant, du point de vue de mes utilisateurs, par exemple, cela ressemblera à ceci:

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  render(){
    return(
      <UserContext.Consumer>
        {({getUsers, users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

Ce que je veux c'est ceci:

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    return(
      <UserContext.Consumer>
        {({users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

Mais bien sûr, l'exemple ci-dessus ne fonctionne pas car les getUsers ne résident pas dans les accessoires de ma vue Utilisateurs. Quelle est la bonne façon de le faire si cela est possible?

26
Gustavo Mendonça

Ok, j'ai trouvé un moyen de faire cela avec une limitation. Avec la bibliothèque with-context, j'ai réussi à insérer toutes mes données de consommation dans les accessoires de mes composants.

Mais, insérer plusieurs consommateurs dans le même composant est une tâche compliquée, vous devez créer des consommateurs mixtes avec cette bibliothèque, ce qui rend le code non élégant et non productif.

Le lien vers cette bibliothèque: https://github.com/SunHuawei/with-context

EDIT: En fait, vous n’avez pas besoin d’utiliser l’API multi-contexte fournie par with-context. En fait, vous pouvez utiliser l’API simple et créer un décorateur pour chacun de vos contextes. déclarez simplement au-dessus de votre classe autant de décorateurs que vous le souhaitez!

3
Gustavo Mendonça

EDIT: Avec l'introduction de react-hooks dans v16.8.0, vous pouvez utiliser le contexte dans les composants fonctionnels en utilisant useContext hook

const Users = () => {
    const contextValue = useContext(UserContext);
    // rest logic here
}

EDIT: À partir de la version 16.6.0. Vous pouvez utiliser le contexte dans la méthode de cycle de vie en utilisant this.context comme 

class Users extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* perform a side-effect at mount using the value of UserContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* render something based on the value of UserContext */
  }
}
Users.contextType = UserContext; // This part is important to access context values

Avant la version 16.6.0, vous pouviez le faire de la manière suivante

Pour utiliser le contexte dans votre méthode de cycle de vie, vous devez écrire votre composant comme suit:

class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    const { users } = this.props;
    return(

            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
    )
  }
}
export default props => ( <UserContext.Consumer>
        {({users, getUsers}) => {
           return <Users {...props} users={users} getUsers={getUsers} />
        }}
      </UserContext.Consumer>
)

En règle générale, vous maintenez un contexte dans votre application et il est logique de regrouper le nom de connexion ci-dessus dans un HOC afin de le réutiliser. Vous pouvez l'écrire comme

import UserContext from 'path/to/UserContext';
const withUserContext = (Component) => {
   return (props) => {
       <UserContext.Consumer>
            {({users, getUsers}) => {
               return <Component {...props} users={users} getUsers={getUsers} />
            }}
       </UserContext.Consumer>
   }
}

et alors vous pouvez l'utiliser comme

export default withUserContext(User);
44
Shubham Khatri

Vous devez passer le contexte dans le composant parent supérieur pour obtenir un accès en tant qu'accessoire dans l'enfant.

0
kprocks