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: '' }
});
}