簡體   English   中英

掛鈎更改 state 不會更新上下文提供程序的值?

[英]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的原因是每個組件( AppNavbar )都有自己的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM