我有2个组件,还有一个上下文提供程序,当我在父级别调用我的钩子时,更改状态并让这2个组件通过上下文获取值没有问题
contex api用法的工作演示,但我在父级调用了更改状态,这不是我想要的 https://stackblitz.com/edit/react-51e2ky?file=index.js
我想用钩子更改内部组件的状态,但是单击navbar login
时看不到该值已更改。
https://stackblitz.com/edit/react-rgenmi?file=Navbar.js
父母:
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>
}
自定义钩子
const loginHook = () => {
const [user, setUser] = React.useState(null)
return {
user,
setUser
}
}
我可以将setUser从父级传递给子级,但是我想避免这种情况,我希望可以使用上下文api并无缝地响应钩子。
答案 0 :(得分:2)
当前,您仅在上下文中设置user
值,这就是为什么获取正确值的原因。
但是,在Navbar.js
组件中,您正在调用loginHook
,这将为该钩子创建一个新的“实例”,并有效地具有其自己的状态。
我建议您也在上下文中添加更新功能,
const App = () => {
const {user, setUser} = loginHook()
return (
<UserContext.Provider value={{ user, setUser}}>
<Navbar />
<Content />
</UserContext.Provider>
);
}
这样,您也可以在孩子中访问setUser
,例如
const Navbar = () => {
const {user, setUser} = React.useContext(userContex)
return <div>{user ? <span>{user.name}</span> : <button onClick={() => {
setUser({
name: 'jane'
})
}}>navbar Login</button>}</div>
}
另外,请注意:最好以use
开始自定义钩子,因为这是编写自己的钩子时的最佳实践。
重要的警告是,这实际上不是一个好习惯。如果您的user
要更改,则所有仅在监听setUser
的组件也将得到更新,从而进行无用的重新渲染。您可以通过使用两种不同的上下文来解决此问题,一种用于值,另一种用于更新程序。您可以了解有关此here
答案 1 :(得分:0)
您不能从孩子那里更改父母的上下文信息,否。您需要从父母那里传递一些东西给孩子,孩子可以用来让父母知道需要更新上下文(例如setUser
的父母副本)。您可以通过prop或在上下文中添加setUser
来做到这一点,尽管我倾向于将其作为对需要设置用户的组件的支持,而不是上下文它们都可以访问的组件
在两个地方都不能使用loginHook
的原因是每个组件(App
和Navbar
)都有自己的user
副本。这是挂钩如何工作的基础。 (如果以某种方式使他们共享状态信息,useState
根本不起作用-所有状态将在所有组件之间共享。)Dan Abramov的A Complete Guide to useEffect
可能是有帮助的读物(更多关于如何钩子和组件的工作要比useEffect
特有的多。)
答案 2 :(得分:0)
您必须注意,自定义钩子不共享实例引用,因此,如果您在App中使用loginHook并在Navbar中使用另一个钩子,它们将创建2个独立的状态和更新程序
现在使用自定义钩子中的setter现在将在上下文中更新状态。
您可以通过编写loginHook来对其进行重组,以便它在内部使用上下文,然后再使用它
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
}
}
现在有多种方法可以编写此代码,但是在上述情况下,最好的方法是根本不使用自定义钩子,因为它无论如何都没有用
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>
}
答案 3 :(得分:-1)
在Navbar.js
中,使用loginHook
钩子将创建一个新的单独状态,该状态不同于App.js
中使用的状态。您需要编写钩子,以便使用上下文而不是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);
然后像这样使用它:
/* App.js */
import {UserProvider} from './UserContext';
const App = () => (
<UserProvider>
<Navbar />
<Content />
</UserProvider>
);
和
/* 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>
}