不想使用Redux进行状态共享怎么办?试试Conetext

207 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

在开发中,无论是React还是Vue我们都会使用热门的状态管理工具,如React使用 React-redux,具体使用参考文章:juejin.cn/post/710389…,但是React-redux很多时候常用在较为大型的项目中,而一些比较小型的项目,共享内容较少,似乎并不适合使用React-redux进行状态管理,在React hooks提出之前,似乎并没有很好的方法,但如今随着hooks的盛行,使用ContextReact-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>
}