携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
在开发中,无论是React还是Vue我们都会使用热门的状态管理工具,如React使用
React-redux,具体使用参考文章:juejin.cn/post/710389…,但是React-redux很多时候常用在较为大型的项目中,而一些比较小型的项目,共享内容较少,似乎并不适合使用React-redux进行状态管理,在React hooks提出之前,似乎并没有很好的方法,但如今随着hooks的盛行,使用Context加React-hooks进行状态的全局共享已经较为常见,适用于小型项目。
Context状态树传参其实是生产者、消费者模式,生产者是指产生数据的模块,消费者是指处理数据的模块,并且生产者与消费者之间需要中介作为缓冲区来缓存数据,这样生产者与消费者之间就不用产生依赖,降低耦合度。其使用Provider将需要共享状态的组件进行包裹,使用useContext进行共享内容的获取,本文以登录demo为例,讲解了如何使用Context进行状态共享,并封装成hooks,方便使用。
1.src下新建auth-provider
定义我们的登录以及注册hooks,包括登录注册请求、并将结果返回。为将登陆、注册方法共享做准备。
export interface User{
name:string,
id:number,
token:string
}
const apiUrl= process.env.REACT_APP_API_URL
const localStorageKey='__auth_provider_token__'
export const getToken=()=>{
window.localStorage.getItem(localStorageKey)
}
export const handelUserResponse=({user}:{user:User})=>{
window.localStorage.setItem(localStorageKey,user.token || '')
return user
}
export const login =(data:{username:string,password:string})=>{
return fetch(`${apiUrl}/login`,{
method:'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify(data)
}).then(
async response=>{
if(response.ok){
return handelUserResponse(await response.json())
}else{
return Promise.reject(data)
}
}
)
}
export const register =(data:{username:string,password:string})=>{
return fetch(`${apiUrl}/register`,{
method:'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify(data)
}).then(
async response=>{
if(response.ok){
return handelUserResponse(await response.json())
}else{
return Promise.reject(data)
}
}
)
}
export const logout= async()=>{
window.localStorage.removeItem(localStorageKey)
}
2.src下新建context文件夹
(1)新建auth-context.tsx
使用
React.createContext创建实例,将子组件使用context.Provider进行包裹,子组件通过children属性传入,同时将定义好的user以及上一个文件内定义好的登陆、注册方法使用value方式进行注入。
同时将我们Context的获取方式封装成一个useAuth的hooks,方便页面直接调用,获取共享的user信息以及登陆注册方法。
import React,{ ReactNode, useState } from 'react';
import * as auth from 'auth-provider'
interface AuthForm{
username:string,
password:string
}
const AuthContext=React.createContext<{
user:auth.User | null,
login:(form:AuthForm)=>Promise<void>,
register:(form:AuthForm)=>Promise<void>,
logout:()=>Promise<void>
} | undefined>(undefined)
AuthContext.displayName='AuthContext'
export const AuthProvider=({children}:{children:ReactNode})=>{
const [user,setUser]=useState<auth.User | null>(null)
const login=(form:AuthForm)=> auth.login(form).then(setUser)
const register=(form:AuthForm)=> auth.register(form).then(setUser)
const logout=()=> auth.logout().then(()=>setUser(null))
return <AuthContext.Provider children={children} value={{user,login,register,logout}}></AuthContext.Provider>
}
export const useAuth=()=>{
const context=React.useContext(AuthContext)
if(!context){
throw new Error('userAuth必须在AuthProvider中使用')
}
return context
}
(2)新建index.tsx
此文件可以对全局的共享状态进行统一管理。
import React, { ReactNode } from 'react';
import { AuthProvider } from './auth-context';
export const AppProviders=({children}:{children:ReactNode})=>{
return <AuthProvider>
{children}
</AuthProvider>
}
(3)项目index.tsx中将需要共享状态的组件进行包裹。
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {AppProviders} from 'context/index';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<AppProviders>
<App />
</AppProviders>
</React.StrictMode>
);
3.登录界面使用
使用auth-context.tsx中的userAuth hook直接获取登陆方法以及用户信息。
import React, { FormEvent } from 'react'
import { useAuth } from '../../context/auth-context';
const apiUrl= process.env.REACT_APP_API_URL
export default function LoginScreen() {
// 此处使用auth-context.tsx中的userAuth hook直接获取登陆方法以及用户信息
const {login,user} =useAuth()
const handelSubmit=(event:FormEvent<HTMLFormElement>)=>{
event.preventDefault()
const username=(event.currentTarget.elements[0] as HTMLInputElement).value
const password=(event.currentTarget.elements[1] as HTMLInputElement).value
login({username,password})
}
return <form onSubmit={handelSubmit}>
{
user?<div>
登录成功,用户名:{user?.name}
</div>:null
}
<div>
<label htmlFor='username'>用户名</label>
<input type='text' id='username'></input>
</div>
<div>
<label htmlFor='password'>密码</label>
<input type='password' id='password'></input>
</div>
<button type={"submit"}>登录</button>
</form>
}