javascript - hook change state doesn't update context provider's value? - Stack Overflow

I have 2 ponent, and a context provider, when I call my hook at parent level, I have no issue changing

I have 2 ponent, and a context provider, when I call my hook at parent level, I have no issue changing the state and having those 2 ponent getting the value via context

working demo of contex api usage but I call change state at parent level which is not what I wanted .js

I want to change state at inner ponent with hook, but I don't see the value been changed when I click on the navbar login.

.js

parent:

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>
}

custom hook

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

  return {
    user,
    setUser
  }
}

I can pass setUser from parent to children but I want to avoid that, I expect I can use context api and react hook seamlessly.

I have 2 ponent, and a context provider, when I call my hook at parent level, I have no issue changing the state and having those 2 ponent getting the value via context

working demo of contex api usage but I call change state at parent level which is not what I wanted https://stackblitz./edit/react-51e2ky?file=index.js

I want to change state at inner ponent with hook, but I don't see the value been changed when I click on the navbar login.

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

parent:

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>
}

custom hook

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

  return {
    user,
    setUser
  }
}

I can pass setUser from parent to children but I want to avoid that, I expect I can use context api and react hook seamlessly.

Share Improve this question asked May 19, 2020 at 9:36 Andre ThonasAndre Thonas 3116 silver badges17 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 4

Currently, you're only setting the user value in the context, which is why getting the correct value will work.

However, in your Navbar.js ponent, you are making a call to loginHook, which will create a new "instance" of that hook, effectively having its own state.

I suggest you add the update function in your context as well, as such

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

}

That way you can access the setUser in your children as well, e.g.

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

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

Also, small note: it's best to start you custom hook with use, as that's a best-practice when writing your own hooks.

Important caveat however, this is not really a good practice. If your user were to change, all ponents that are only listening to setUser will also get an update an thus do a useless rerender. You can solve this by using two different contexts, one for the value, and one for the updater. You can read more about this here

You cannot change the parent's context information from the child, no. You'll need to pass something to the child from the parent that the child can use to let the parent know that the context needs to be updated (such as the parent's copy of setUser). You can do that via a prop or by adding setUser to the context, though I'd lean toward just doing it as a prop to ponents that need to be able to set the user, rather than context they all have access to.

The reason using loginHook in both places didn't work is that each ponent (App and Navbar) has its own copy of user. This is fundamental to how hooks work. (If it somehow made them share the state information, useState wouldn't work at all — all state would be shared across all ponents.) Dan Abramov's A Complete Guide to useEffect may be a helpful read (it's more about how hooks and ponents work than it is specifically about useEffect).

You must note that custom hooks do not share instance references, so if you use the loginHook in App and another one in Navbar, they will create 2 separate states and updaters

Now using a setter from custom hook will now update the state in context.

You can restructure this by writing your loginHook so that it internally uses context and then using it

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
  }
}

Now there are multiple ways to write this code, However the best way in the above scenario is not use a custom hook at all since it anyway is not useful

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>
}

In your Navbar.js you use your loginHook hook which will create a new separate state that is different from the state used in your App.js. You need to write your hook so that is uses the context instead of 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);

Then use it like that:

/* App.js */

import {UserProvider} from './UserContext';

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

and

/* 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>
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744801814a4594537.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信