![](/img/trans.png)
[英]Why can't update a value in a child component (with react context) when a provider component parent state change?
[英]hook change state doesn't update context provider's value?
我有 2 個組件和一個上下文提供程序,當我在父級別調用我的鈎子時,更改 state 並讓這 2 個組件通過上下文獲取值沒有問題
contex api 使用的工作演示,但我在父級調用更改 state 這不是我想要的 https://stack2blitz.com/edit/jsfile?
我想用鈎子更改內部組件的 state ,但是當我單擊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>
);
}
導航欄.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 並無縫響應鈎子。
目前,您只是在上下文中設置user
值,這就是為什么獲得正確的值會起作用。
但是,在您的Navbar.js
組件中,您正在調用loginHook
,這將創建該掛鈎的新“實例”,有效地擁有自己的 state。
我建議您在您的上下文中添加更新 function ,因此
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
的組件也將獲得更新,從而進行無用的重新渲染。 您可以通過使用兩種不同的上下文來解決此問題,一種用於值,一種用於更新程序。 您可以在此處閱讀有關此內容的更多信息
您不能從孩子那里更改父母的上下文信息,不。 您需要從父級傳遞一些東西給子級,子級可以使用這些內容讓父級知道需要更新上下文(例如父級的setUser
副本)。 您可以通過道具或通過將setUser
添加到上下文來做到這一點,盡管我傾向於將其作為需要能夠設置用戶的組件的道具,而不是他們都可以訪問的上下文。
在兩個地方都使用loginHook
的原因是每個組件( App
和Navbar
)都有自己的user
副本。 這是鈎子如何工作的基礎。 (如果它以某種方式讓他們共享 state 信息, useState
將根本不起作用 - 所有 state 將在所有組件之間共享。)Dan Abramov's A Complete Guide to useEffect
可能會有所幫助(更多關於鈎子和組件如何工作而不是專門關於useEffect
)。
您必須注意自定義掛鈎不共享實例引用,因此如果您在 App 中使用 loginHook 而在 Navbar 中使用另一個,它們將創建 2 個獨立的狀態和更新程序
現在使用自定義鈎子中的設置器現在將在上下文中更新 state。
您可以通過編寫 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>
}
在您的Navbar.js
,您使用loginHook
鈎子,它將創建一個新的單獨的 state,它與您 App.js 中使用的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>
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.