简体   繁体   English

ReactJS 在使用 useEffect 钩子获取后重新渲染上下文

[英]ReactJS Context re-render after a fetch with useEffect hook

I am currently trying to pass data with my context provider through the entire application but the hook that triggers, only returns it's initial value and doesn't re-trigger .我目前正在尝试通过整个应用程序使用我的context provider传递数据,但触发的钩子仅返回它的初始值并且不会重新触发

This is my application layout inside my main App component which doesn't hold any state.这是我的主要App组件中的应用程序布局,它不包含任何 state。

class App extends Component {
  render() {
    return (
      <div>
        <BrowserRouter>
          <UserContextProvider>
            <div className="container">
              <Header />
              <Switch>
                <Route exact path="/" component={Home} />
                <Route exact path="/login" component={LoginPage} />
                <PrivateRoute exact path="/secret-page" component={SecretPage} />
              </Switch>
            </div>
          </UserContextProvider>
        </BrowserRouter>
      </div>
    );
  }
}

export default App;

Whenever the App component renders for the first time, it triggers the UserContextProvider which fires an onEffect hook.每当App组件第一次呈现时,它都会触发UserContextProvider ,它会触发一个onEffect钩子。 This hook tries to fetch some data but since the user is not logged in, it just returns it's original value [] .这个钩子试图获取一些数据,但由于用户没有登录,它只是返回它的原始值[]

Here is the Context Provider这是上下文提供者

const UserContextProvider = ({children}) => {
  const [userSettings, setUserSettings] = useState([]);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  useEffect(() => {
    if (userid !== null) {
      fetch(
        process.env.REACT_APP_API_URL + "/authenticated/get-permissions",
        requestOptions
      )
        .then(response => response.json())
        .then(response => {
          setUserSettings(response.data);
          setIsLoggedIn(true);
        })
        .catch(error => console.log(error));
    } else {
      console.log(userSettings);
      setIsLoggedIn(false);
    }
  }, []);
  return (
    <UserContext.Provider value={userSettings}>{children}</UserContext.Provider>
  );
};

Now when the code above fires initially, the userid value is null but after authentication in the LoginPage child component, it changes to a token.现在,当上面的代码最初触发时, userid值为null但在LoginPage子组件中进行身份验证后,它会更改为令牌。 My issue is that even though userid 's value changes after login, the hook doesn't fire again.我的问题是,即使userid的值在登录后发生变化,钩子也不会再次触发。

I just want to mention that I get the userid value from local storage in a separate file and import it.我只想提一下,我从本地存储中的单独文件中获取userid值并将其导入。

I've tried setting the userid value in the empty array argument inside the function but that doesn't work.我尝试在 function 内的空数组参数中设置userid值,但这不起作用。

How can I make my useEffect hook to fire again after the userid value has changed?userid值更改后,如何使我的useEffect挂钩再次触发?

--- EDIT----- - - 编辑 - - -

userid is import from another file called fetchRequestOptions in this way: userid是以这种方式从另一个名为fetchRequestOptions的文件中导入的:

import {requestOptions, userid} from "./fetchRequestOptions";

Inside my fetchRequestOptions file I have the following code:在我的fetchRequestOptions文件中,我有以下代码:

let userid = localStorage.getItem("userid");
let accessToken = localStorage.getItem("accesstoken");
let requestOptions ={ <my request options are here>}

In order for the hook that uses userid to re-run when it changes, you need to keep it inside a state.为了使用userid的钩子在更改时重新运行,您需要将其保存在 state 中。 Then, LoginPage needs to be connected to the Context and use setUserid to update its value.然后, LoginPage需要连接到 Context 并使用setUserid来更新它的值。

const UserContextProvider = ({ children }) => {
    const [userSettings, setUserSettings] = useState([]);
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    // If you are reading directly from localstorage you might
    // want to use localStorage.getItem("userid") as the initial value
    const [userid, setUserid] = useState(null);

    useEffect(() => {
        // ...
    }, [userid]);

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

The key is that userid needs to be inside a state, this way it can trigger a new render in any Component where you need its latest value.关键是userid需要在 state 中,这样它可以在您需要其最新值的任何组件中触发新的渲染。

You might find it cleaner to have a specific custom hook, inside your Context, that handles the logic for the userid (fetch and update).您可能会发现在您的 Context 中拥有一个特定的自定义钩子会更简洁,它处理用户 ID 的逻辑(获取和更新)。 This is a good option but it only makes userid available inside this context.这是一个不错的选择,但它只使userid在此上下文中可用。 If you need to use it somewhere else you will need to export it from the context value prop.如果您需要在其他地方使用它,则需要从上下文value道具中导出它。

const useUserid = () => {
    const [userid, setUserid] = useState(null);

    useEffect(() => {
        const fetched_userid = //...
        setUserid(fetched_userid);
    }, []);

    return userid;
};

const UserContextProvider = ({ children }) => {
    // ...
    const userid = useUserid();

    useEffect(() => {
        // ...
    }, [userid]);

    return (
        <UserContext.Provider value={userSettings}>{children}</UserContext.Provider>
    );
};

Passing an empty array [] in an useEffect hook means that (as per React documentation)在 useEffect 挂钩中传递一个空数组 [] 意味着(根据 React 文档)

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument.如果你想运行一个效果并且只清理一次(在挂载和卸载时),你可以传递一个空数组([])作为第二个参数。 This tells React that your effect doesn't depend on any values from props or state, so it never needs to re-run这告诉 React 你的效果不依赖于 props 或 state 的任何值,所以它永远不需要重新运行

So, we need to pass props/state that acts as trigger for effect to re-render.因此,我们需要传递作为重新渲染效果触发器的道具/状态。

pass userid as an argument in the dependency array of the useEffect.userid作为参数传递到 useEffect 的依赖数组中。 This ensures the effect is run whenever userid changes.这样可以确保在userid更改时运行效果。

  useEffect(() => {
   ...
  }, [userid]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM