如何在React的父组件中检测本地存储集

问题描述

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

身份验证服务:

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,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中的表单登录时,它不会呈现<Menu>Logged In</Menu>中的菜单App.js。当我们在本地存储get currentUserValue () { return localStorage.getItem('token') }中设置令牌值并登录后呈现菜单时,是否可以重新渲染?

解决方法

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

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

存储挂钩将创建您读写令牌的状态。它还根据令牌中的值更新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函数将用token更新setToken的值,而localStorage则将更新token

从挂钩中返回loginlogouttoken函数,因此login仅可通过logout和{{1 }} 职能。当然,可以通过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,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保持同步,即使您浏览或刷新页面也是如此。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...