自定义Hook状态管理

2,124 阅读2分钟

React项目中使用自定义Hook+useContext进行状态管理。

自定义Hook

自定义Hook必须是以use开头的函数,否在React语法检查不会让你过。由于ReactuseState在函数执行时会被顺序存储在链表中,所以自定义Hook只能在函数的第一层调用。不能放在条件判断中,因为判断条件会影响useState的执行顺序,导致存储进链表的顺序受到影响。

代码示例

使用自定义Hook对record的一系列操作封装为一个自定义Hook函数。数据存储在useState中。并暴露出操作状态的一系列方法。就可做到类似Vuemultiple功能

//定义record类型
export type RecordItem={
	id:number
    tagId:number
    note:string
    createdAt:string
}

//定义useRecord返回的类型
export type RecordAction = {
	records:RecordItem[]
	createRecord:(record: RecordItem)=>void
    deleteRecord:(id: number)=>void
    updateRecord:(id: number, record: RecordItem)=>void
    findRecord:(id: number)=>void
}

const useRecords = (): RecordAction => {
  const [records, setRecords] = useState<RecordItem[]>(recordList);
  //增
  const createRecord = (record: RecordItem) => {
    if (!record.createAt)
    //使用dayjs格式化记录创建时间时间
      record.createAt = dayjs(new Date()).format('YYYY-MM-DDTHH:mm:ss');
    record.id = createId(); //生成ID
    setRecords((rs) => [...rs, record]);
  };
  //删
  const deleteRecord = (id: number) => {
    setRecords((rs) => rs.filter((r) => r.id !== id));
  };
  //改
  const updateRecord = (id: number, record: RecordItem) => {
    setRecords((rs) => rs.map(r => r.id === id ? {id, ...record} : r));
  };
  //查
  const findRecord = (id: number) => {
    return records.filter((r) => r.id === id)[0];
  };
  return {
    createRecord,
    deleteRecord,
    updateRecord,
    findRecord,
    records
	}
}

export default useRecords

这样就可以在项目中使用useRecord来对记录的状态进行管理。

注意你需要保证整个项目useReacord只调用了一次,毕竟它只是一个函数,每次调用都会创建新的state,如果在不同的组件中多次调用这会导致数据不同步。

那么如何做到只调用一次useRecord呢,可以使用useContext在最上顶成组件中将useRecord()执行后返回的对象(操作records的方法)传递到子组件中即可。

context.tsx

import {createContext} from 'React'
import {RecordAction} from 'hooks/useRecord' //createContext接受的类型
const ContextReacrd = createContext<RecordAction>({}) 
export default ContextRecord

App.tsx

import React from 'react'
import ContextRecord from '/../ContextRecord'
import useRecord from 'hooks/useRecord'
const App = ()=>{
	const recordAction = useRecord() //返回操作record的方法
	return(
    	<ContextRecord.Provider value={recordAction}>
            <Child />
        </ContextRecord.Provider>
    )
}

这样在后代组件中就可以使用useContext来拿到操作state的函数。

Child.tsx

import React,{useContext} from 'react'
import ContextRecord from '/../ContextRecord'
const Child = ()=>{
	const recordAction = useContext(ContextRecord)//拿到操作record的函数 
    return (
    	<Child2 />
    )
}

export default Child