场景一:共享状态逻辑类似于vuex,redux

import { useState } from 'react';
// 自定义钩子useCounter
function useCounter(initialCount) {

//useState可以类比vuex中的state定义
const [count, setCount] = useState(initialCount); 

// 以下的setCount方法可以类比vuex中的mutation,组件可以直接调用这个方法,而vuex中组件是不可以直接调用mutation的,需要通过action桥梁
const increment = () => setCount(count + 1);   
const decrement = () => setCount(count - 1);
const reset = () => setCount(initialCount);
return { count, increment, decrement, reset };
}
export default useCounter;

在上面的代码中,我们创建了一个名为useCounter的自定义钩子,它接受一个初始计数值initialCount。钩子返回一个对象,该对象包含计数状态以及用于增加、减少和重置计数的方法。

在组件中使用这个钩子:组件可以直接使用hook的state变量和方法,在vuex中state变量需要通过mapGetters或者this.$store.state.count才能获取,方法只能调用store中的action,而不能直接调用.

import React from 'react';
import useCounter from './useCounter';

const CounterComponent = () => {
const { count, increment, decrement, reset } = useCounter(0);

return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};
export default CounterComponent;

场景二:共享js逻辑:使用hook封装echarts,dom节点的创建,以及echart的初始化代码都是共用的

echarts中有公共代码,dom节点的创建、图表初始化init以及,可以将这两块封装成一个自定义的hook

export const useCharts = (): [RefObject<HTMLDivElement>, echarts.EChartsType | undefined] => { //创建自定义hook:useCharts
const chartRef = useRef<HTMLDivElement>(null). //使用useRef创建dom节点
const [chartInstance, setChartInstance] = useState<echarts.EChartsType>()。//定义echart实例
useEffect(() => {
const chart = echarts.init(chartRef.current as HTMLElement) //echart初始化
setChartInstance(chart)//这段代码的含义是什么?由于return 的时候return 出去的是chartInstance这个变量,chartInstance变量初始值为空,要想给chartInstance变量赋值必须要使用set方法
}, [])

return [chartRef, chartInstance]  //return 出chartRef和chartInstance这两个变量供组件使用
}
组件使用:
// 初始化折线图
const [lineRef, lineChart] = useCharts()
// 初始饼图
const [pieRef1, pieChart1] = useCharts()
lineChart?.setOption({
 title: {
 text: '订单和流水走势图'
 },
tooltip: {
trigger: 'axis'
},
legend: {
data: ['订单', '流水']
}
}
场景三:封装输入框的状态管理:一般不需要这样使用,并不会很方便,除非多个页面共享同一个状态。
由于react在表单组件中无法实现双向绑定,如果表单的值改变了,必须要使用set方法进行修改更新,这样就需要在每个表单组件中都编写一个change方法来修改值,会增加代码量,那么如果使用hook来封装就会提升效率:
创建一个自定义hook来管理输入框的状态:

import { useState } from 'react';
function useInput(initialValue) {
const [value, setValue] = useState(initialValue); //定义表单绑定的值
const handleChange = (event) => { //定义表单的值更新方法
setValue(event.target.value);
};
return {
value,
onChange: handleChange
};
}
export default useInput;

在组件中使用这个自定义hook:

import React from 'react';
import useInput from './useInput';
const MyInput = () => {
const { value, onChange } = useInput('');
return (
<input
type="text"
value={value}
onChange={onChange}
/>
);
};
export default MyInput;

为什么不直接封装一个onChane函数,在组件中直接调用呢,因为这样的话表单的绑定值就需要在组件内部进行定义:const [value, setValue] = useState(initialValue); ,函数无法在外部使用setValue方法。

场景四:表单验证,同场景二一样,都是封装公用js逻辑。

react复用逻辑时,什么时候封装hook,什么时候封装子组件?

涉及ui部分复用时,要封装成子组件,不能使用hook.

自定义Hook和JS方法的区别:

  • ‌复用性‌:自定义Hook更适合复用逻辑,特别是跨组件的复用。而JS方法更适合在单个组件内部使用。
  • ‌封装程度‌:自定义Hook封装了完整的逻辑单元,提供了清晰的接口。而JS方法可能只是组件内部的一个简单函数调用。
  • ‌维护性‌:自定义Hook使得逻辑的更新和维护更加集中和方便,而JS方法可能需要更多的上下文来理解和维护。

总结:自定义hook和封装普通js函数有相通之处,很多情况下是可以替代使用的

1、涉及到同一个状态被多处组件使用时,使用hook更方便,类似于vuex

2、涉及到js逻辑共用时,分两种情况,如果共用的js逻辑在同一个函数中,并且只需要传递不同的参数就能实现功能,此时就进行简单的函数封装即可。如果涉及到共用js逻辑比较分散,例如表单的验证,echarts图表,就可以封装成hook.

Redux与自定义Hooks的区别

  • ‌全局与局部‌:Redux适用于全局状态管理,而自定义Hooks适用于局部状态管理。
  • ‌集成方式‌:Redux需要额外的配置和中间件,而自定义Hooks直接集成在React中,使用更加简单和直观。
  • ‌性能‌:在简单的应用中,自定义Hooks通常性能更好,因为它减少了不必要的渲染和状态提升。

综上所述,选择Redux还是自定义Hooks取决于你的具体需求和应用规模。对于全局状态管理和大型项目,Redux是更好的选择;而对于局部状态管理和简单应用,自定义Hooks则更为合适。