【问题标题】:How to detect localstorage set in parent component of React如何检测 React 父组件中的本地存储集
【发布时间】:2020-09-27 16:08:27
【问题描述】:

我有一个身份验证服务,可以在成功登录后设置本地存储令牌

身份验证服务:

async function login(email, password, remember) {
  const data = await ApiClient.post(`/api/auth`, { 
    email: email, password: password, remember: remember 
  });
  localStorage.setItem('token', data.token);
  return data.token;
}

async function logout() {
  localStorage.removeItem('token');
}

export const authenticationService = {
  login,
  logout,
  get currentUserValue () { return localStorage.getItem('token') }
}

App.js

{ authenticationService.currentUserValue &&
<Menu>Logged In</Menu> }
<Switch>
  <PrivateRoute path="/dashboard" component={Dashboard} />
  <Route path="/login" component={Login} />
  <Route component={Error404} />
</Switch>

Login.js

async function login(e) {
    e.preventDefault();
    try {
      const token = await authenticationService.login(email, password, remember);
      setErrorMsg('');
      history.push('/dashboard');
    } catch (err) {
      if(err.response.status === 401) {
        setErrorMsg('Unauthorized access, please login again.');
      }
    }
  }
<form className={classes.form} noValidate onSubmit={login}>
  <TextField value={email} /> 
  <TextField value={password} /> 
  <FormControlLabel
    control={<Checkbox value="remember" color="primary" />}
    label="Remember me"
    onChange={(e) => setRemember(e.target.value)}
    value={remember}
  />
  <Button type="submit">Sign In</Button>
</form>

当我使用Login.js 中的表单登录时,它不会从App.js 呈现菜单&lt;Menu&gt;Logged In&lt;/Menu&gt;。当我们在 localstorage get currentUserValue () { return localStorage.getItem('token') } 中设置令牌值并在登录后渲染菜单时,有没有办法重新渲染?

【问题讨论】:

  • 我建议在您的商店/状态中的某处反映该值,您可以使用useEffect 挂钩或使用storage event 上的侦听器来设置它。在 React 中很难跟踪存储在 React 组件之外的数据,因为它不会被监视以触发重新渲染。

标签: javascript reactjs react-router react-router-dom react-router-v4


【解决方案1】:

您需要将令牌存储在某个状态的某个地方。然后根据令牌的状态呈现您的视图。

创建挂钩并分离一些关注点。身份验证现在需要获取和存储令牌。在这些钩子中拆分这两个:存储钩子和身份验证钩子。

存储挂钩将创建一个您可以读取和写入令牌的状态。它还会根据令牌中的值更新localStorage 项。

返回tokensetToken 函数供其他组件或挂钩使用。

const useTokenStorage = () => {
  const [ token, setToken ] = useState(() => {
    const token = localStorage.getItem('token');
    return token !== null ? token : ''
  });

  useEffect(() => {
    if (token === '') {
      localStorage.removeItem('token');
    } else {
      localStorage.setItem('token', token);
    }
  }, [token]);

  return [ token, setToken ];
};

身份验证服务挂钩将使用存储挂钩来设置令牌的值。 loginlogout 函数将使用setToken 更新token 的值,而setToken 又将更新localStorage

从钩子返回tokenloginlogout函数,所以token只能通过loginlogout函数更新。当然可以通过token 属性阅读。

const useAuthenticationService = () => {
  const [ token, setToken ] = useTokenStorage();

  const login = async (email, password, remember) => {
    const data = await ApiClient.post(`/api/auth`, { 
      email: email, password: password, remember: remember 
    });
    setToken(data.token);
  };

  const logout = () => {
    setToken('');
  };

  return { token, login, logout };
};

在组件中使用useAuthenticationService 挂钩,您需要根据token 确定要呈现的内容,就像在您的App.js 中一样

// App.js
const AppComponent = () => {
  const { token } = useAuthenticationService();

  return (
    { token && <Menu>Logged In</Menu> }
    <Switch>
      <PrivateRoute path="/dashboard" component={Dashboard} />
      <Route path="/login" component={Login} />
      <Route component={Error404} />
    </Switch>
  );
};

并在需要更新令牌状态的地方使用loginlogout 函数。

// Login.js
const LoginComponent = () => {
  const { login, logout } = useAuthenticationService();

  async function handleSubmit(e) {
    e.preventDefault();
    try {
      await login(email, password, remember);
      history.push('/dashboard');
    } catch (err) {
      if(err.response.status === 401) {
        setErrorMsg('Unauthorized access, please login again.');
      }
    }
  }

  render(
    <form className={classes.form} noValidate onSubmit={handleSubmit}>
      <TextField value={email} /> 
      <TextField value={password} /> 
      <FormControlLabel
        control={<Checkbox value="remember" color="primary" />}
        label="Remember me"
        onChange={(e) => setRemember(e.target.value)}
        value={remember}
       />
      <Button type="submit">Sign In</Button>
    </form>
  );
}

token 现在应该与localStorage 同步,即使在您导航或刷新页面时也是如此。

【讨论】:

  • 我刚试过这个,登录和history,push('/dashboard')后似乎仍然没有呈现更改令牌。它仅在我刷新仪表板页面上的整个页面时出现。我认为挑战在于App.jsLogin.js 的父级,并且在Login.js 中调用该函数不会触发App.js 上父函数上组件的重新渲染
  • 好的。我通过将登录函数从App.js(父级)传递给Login.js(子级)来实现这个工作,因此子级实际上调用了从父级实例化的登录函数,该函数触发了App.js 的重新渲染,因为父级的状态发生了变化级别
  • 啊,好吧!做得好。您是否也考虑过使用 Context API?
  • 上下文 API 是否比从父级传递函数更好?
  • 嗯,仅当您可能在更深的嵌套子项中使用令牌或仅在无法将其作为道具传递的地方使用令牌时。不是更好,只是更灵活一点。
猜你喜欢
  • 1970-01-01
  • 2020-07-28
  • 1970-01-01
  • 2020-08-27
  • 1970-01-01
  • 1970-01-01
  • 2021-09-26
  • 2020-07-23
  • 1970-01-01
相关资源
最近更新 更多