Typescript + useContext + useReducer实现全局状态存储及更新

场景:
React应用需要实现登录界面与Dashboard界面的切换。
使用一个全局变量存储用户信息,如果为空,则进入登录界面;否则进入Dashboard界面。

实现方法:
通过useContext保存全局用户信息,如果全局user对象的email字段为空,就说明退出登录,否则为登录状态。

UserContext

import React, { useContext } from "react";

export type User = {
    email: string
}

export type Action =
 | { type: 'login', payload: User  }
 | { type: 'logout', payload: User }

export const initState: User = {
    email: ''
}

export function userReducer(state:User, action: Action){
    switch (action.type) {
        case 'login': //login with user info
            return {
                ...state,
                email: action.payload.email
            }
        case 'logout': //logout return true
            return initState;
        default:
            return state;
    }
}

const UserContext = React.createContext<{
    state: User;
    dispatch: React.Dispatch<Action>;
    }>({
        state: initState,
        dispatch: () => undefined,
    });

export default UserContext;

index.tsx

import React, { useEffect, useState, useReducer } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import LoginForm from './Login';
import UserContext, { User, Action, userReducer, initState } from './context/UserContext';
import styled from 'styled-components';

const LoginBox = styled.div`
  position: absolute;
  top: 25%; 
  left: calc(50% - 100px);
  padding: 25px;
  border: none;
  box-shadow: 0 2px 4px rgb(0 0 0 / 10%), 0 8px 16px rgb(0 0 0 / 10%);
`

function Root() {
  const [state, dispatch] = useReducer(userReducer, initState);
  return(
    <UserContext.Provider value={{ state, dispatch: dispatch }}>
      {!state.email? <LoginBox><LoginForm /></LoginBox>
      :<App></App> }
    </UserContext.Provider>
  )
}

ReactDOM.render(
  <React.StrictMode>
    <Root />
  </React.StrictMode>,
  document.getElementById('root')
);

Login.tsx
此处在点击login button时调用dispath,type为"login"

import React, { useContext } from 'react';
import { Form, Input, Button, Checkbox } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import "antd/dist/antd.css";
import styled from 'styled-components';
import UserContext from './context/UserContext';

const responseGoogle = (response: any) => {
    console.log(response);
}

const LoginBtn = styled(Button)`
    width: 100%;
`

const NormalLoginForm = () => {
    const userContext = useContext(UserContext);

    const onFinish = (values: any) => {
      console.log('Received values of form: ', values);
      userContext.dispatch({
        type: 'login',
        payload: { email: values.username }
      });
    };
  
    return (
      <Form
        name="normal_login"
        className="login-form"
        initialValues={{ remember: true }}
        onFinish={onFinish}
      >
        <Form.Item
          name="username"
          rules={[{ required: true, message: 'Please input your Username!' }]}
        >
          <Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="Username" />
        </Form.Item>
        <Form.Item
          name="password"
          rules={[{ required: true, message: 'Please input your Password!' }]}
        >
          <Input
            prefix={<LockOutlined className="site-form-item-icon" />}
            type="password"
            placeholder="Password"
          />
        </Form.Item>
        <Form.Item>
          <Form.Item name="remember" valuePropName="checked" noStyle>
            <Checkbox>Remember me</Checkbox>
          </Form.Item>
        </Form.Item>
        <Form.Item>
            <LoginBtn type="primary" htmlType="submit" className="login-form-button">
            Log in
            </LoginBtn>
        </Form.Item>        
      </Form>
    );
  };


export default NormalLoginForm;

同样的,如果要退出登录,可以通过以下代码实现

 const userContext = useContext(UserContext);
  const onLogout = () => {
    userContext.dispatch({
      type: 'logout',
      payload: { email: '' }
    });
  }
posted @ 2021-08-04 23:14  老胡Andy  阅读(658)  评论(0编辑  收藏  举报