J'ai 2 composants et un fournisseur de contexte, lorsque j'appelle mon hook au niveau du parent, je n'ai aucun problème à changer l'état et à faire en sorte que ces 2 composants obtiennent la valeur via le contexte

Démonstration fonctionnelle de l'utilisation de l'API contextuelle mais j'appelle le changement d'état au niveau du parent, ce qui n'est pas ce que je voulais https://stackblitz.com/edit/react-51e2ky?file=index.js

Je veux changer d'état au niveau du composant interne avec hook, mais je ne vois pas que la valeur a été modifiée lorsque je clique sur navbar login.

https://stackblitz.com/edit/react-rgenmi?file=Navbar.js

Parente:

const App = () => {
  const {user} = loginHook()
    return (
      <UserContext.Provider value={user}>
        <Navbar />
        <Content />
      </UserContext.Provider>
    );

}

Navbar.js

const Navbar = () => {
  const user = React.useContext(userContex)
  const {setUser} = loginHook()

  return <div>{user ? <span>{user.name}</span> : <button onClick={() => {
    setUser({
      name: 'jane'
    })
  }}>navbar Login</button>}</div>
}

Crochet personnalisé

const loginHook = () => {
  const [user, setUser] = React.useState(null)

  return {
    user,
    setUser
  }
}

Je peux passer setUser du parent aux enfants mais je veux éviter cela, je m'attends à pouvoir utiliser l'API contextuelle et réagir de manière transparente.

4
Andre Thonas 19 mai 2020 à 12:36

5 réponses

Meilleure réponse

Actuellement, vous ne définissez la valeur user que dans le contexte, c'est pourquoi obtenir la valeur correcte fonctionnera.

Cependant, dans votre composant Navbar.js, vous faites un appel à loginHook, qui créera une nouvelle "instance" de ce hook, ayant effectivement son propre état.

Je vous suggère d'ajouter la fonction de mise à jour dans votre contexte également, en tant que telle

const App = () => {
  const {user, setUser} = loginHook()
    return (
      <UserContext.Provider value={{ user, setUser}}>
        <Navbar />
        <Content />
      </UserContext.Provider>
    );

}

De cette façon, vous pouvez également accéder au setUser de vos enfants, par exemple

const Navbar = () => {
  const {user, setUser} = React.useContext(userContex)

  return <div>{user ? <span>{user.name}</span> : <button onClick={() => {
    setUser({
      name: 'jane'
    })
  }}>navbar Login</button>}</div>
}

Petite remarque également: il est préférable de démarrer votre hook personnalisé avec use, car c'est une bonne pratique lors de l'écriture de vos propres hooks.

Attention cependant, ce n'est pas vraiment une bonne pratique. Si votre user devait changer, tous les composants qui n'écoutent que setUser recevront également une mise à jour et effectueront donc un rendu inutile. Vous pouvez résoudre ce problème en utilisant deux contextes différents, un pour la valeur et un pour le programme de mise à jour. Vous pouvez en savoir plus sur ici

2
JDansercoer 19 mai 2020 à 09:51

Dans votre Navbar.js, vous utilisez votre hook loginHook qui créera un nouvel état distinct qui est différent de l'état utilisé dans votre App.js. Vous devez écrire votre hook pour qu'il utilise le contexte au lieu de useState:

/* UserContext.js */

const UserContext = createContext();

export const UserProvider = ({children}) => {
    const [user, setUser] = useState(null);

    return (
        <UserContext.Provider value={{user, setUser}}>
            {children}
        </UserContext.Provider>
    );
}

export const useLogin = () => useContext(UserContext);

Ensuite, utilisez-le comme ça:

/* App.js */

import {UserProvider} from './UserContext';

const App = () => (
    <UserProvider>
        <Navbar />
        <Content />
    </UserProvider>
);

Et

/* Navbar.js */

import {useLogin} from './UserContext';

const Navbar = () => {
  const {user, setUser} = useLogin();

  return <div>{user ? <span>{user.name}</span> : <button onClick={() => {
    setUser({
      name: 'jane'
    })
  }}>navbar Login</button>}</div>
}
-1
trixn 19 mai 2020 à 09:47

Au lieu d'appeler loginHook dans différents composants, appelez-le une fois dans le composant App et passez user et setUser à d'autres composants via le contexte.

Chaque fois que vous appelez loginHook, une nouvelle instance de ce hook est créée, ce qui n'est pas ce que vous voulez. Vous ne voulez qu'une seule instance dans votre application et les données partagées via le contexte du composant parent vers d'autres composants.

Si vous voulez appeler la fonction setUser à partir du composant Navbar, transmettez la fonction setUser au composant Navbar via le contexte, puis appelez cette fonction setUser pour mettre à jour l'état correctement

Voir la démonstration fonctionnelle

0
Yousaf 19 mai 2020 à 10:11

Vous devez noter que les hooks personnalisés ne partagent pas les références d'instance, donc si vous utilisez le loginHook dans App et un autre dans Navbar, ils créeront 2 états et mises à jour séparés

Désormais, l'utilisation d'un setter à partir d'un hook personnalisé mettra à jour l'état en contexte.

Vous pouvez restructurer cela en écrivant votre loginHook afin qu'il utilise le contexte en interne, puis en l'utilisant

const App = () => {
  const [user, setUser] = useState();
    return (
      <UserContext.Provider value={{user, setUser}}>
        <Navbar />
        <Content />
      </UserContext.Provider>
    );

}

const Navbar = () => {
  const {user, setUser} = loginHook();

  return <div>{user ? <span>{user.name}</span> : <button onClick={() => {
    setUser({
      name: 'jane'
    })
  }}>navbar Login</button>}</div>
}
const loginHook = () => {
  const {user, setUser} = React.useContext(UserContext)

  return {
    user,
    setUser
  }
}

Maintenant, il existe plusieurs façons d'écrire ce code, mais la meilleure façon dans le scénario ci-dessus n'est pas du tout d'utiliser un hook personnalisé car il n'est de toute façon pas utile

const App = () => {
  const [user, setUser] = useState();
    return (
      <UserContext.Provider value={{user, setUser}}>
        <Navbar />
        <Content />
      </UserContext.Provider>
    );

}

const Navbar = () => {
  const {user, setUser} = React.useContext(UserContext);

  return <div>{user ? <span>{user.name}</span> : <button onClick={() => {
    setUser({
      name: 'jane'
    })
  }}>navbar Login</button>}</div>
}
0
Shubham Khatri 19 mai 2020 à 09:48

Vous ne pouvez pas modifier les informations de contexte du parent à partir de l'enfant, non. Vous devrez transmettre quelque chose à l'enfant du parent que l'enfant peut utiliser pour lui faire savoir que le contexte doit être mis à jour (comme la copie parent de setUser). Vous pouvez le faire via un accessoire ou en ajoutant setUser au contexte, même si je pencherais pour le faire simplement comme accessoire pour les composants qui doivent pouvoir définir l'utilisateur, plutôt que le contexte auquel ils ont tous accès à.

La raison pour laquelle l'utilisation de loginHook aux deux endroits n'a pas fonctionné est que chaque composant (App et Navbar) a sa propre copie de user. Ceci est fondamental pour le fonctionnement des hooks. (Si cela leur faisait en quelque sorte partager les informations d'état, useState ne fonctionnerait pas du tout - tout l'état serait partagé entre tous les composants.) Un guide complet de useEffect peut être une lecture utile (il s'agit davantage du fonctionnement des hooks et des composants que de {{ X6}}).

0
T.J. Crowder 19 mai 2020 à 09:40