Skip to main content

Hooks

Hooks

Hooks 是React 在 v16.7版本后推出的全新特性,用来对函数式组件的内部状态进行维护。

Hooks 使用规则

  1.  只在顶层调用Hook

    不要在循环,条件或嵌套函数中调用 Hook 。 通过遵循此规则,可以确保每次组件渲染时都以相同的顺序调用 Hook 。

  2. 只在 React Functions 调用 Hooks

在Lint中,我们通常会引入eslint-plugin-react-hooks这个ESlint插件来强制执行这两个规则。

基础 Hooks

State Hook

State Hook允许您将 React state(状态) 添加到函数式组件中。

useState每次修改都会生成一个新的state。

  1. State的定义

    const _count = useState(0);
    const count = _count[0];
    const setCount = _count[1];

    useState的第一个元素是当前值,第二个元素是允许我们更新第一项元素值的函数。

    使用 [0][1] 访问它们有点令人困惑,因为它们有特定的含义。如果你熟悉ES6中的数组解构,那么就可以使用下面这种写法。

    const [count, setCount] = useState(0);
  2. State的修改

    <button onClick={() => setCount(count + 1)}>
    Click me
    </button>
  3. State的使用

    <p>You clicked {count} times</p>

Ref Hook

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传递的参数(initialValue)。返回的对象将存留在整个组件的生命周期中。

// 定义
const demo = useRef(0);

// 修改
demo.current = 1;

console.log(demo);

// {
// current:1
// }

State Hook vs Ref Hook

State 和 Ref 发生数据变化时的区别:

State HookRef Hook
数据重新生成state只改变值
画面画面重新渲染不会重新渲染

示例:

const [test, setTest] = useState(0);
const test2 = useRef(0);
const log = () => {
setTimeout(() => {
console.log({test});
console.log({test2: test2.current});
}, 3000);
};


{test}/{test2.current}

<Button onClick={() => { setTest(test + 1); test2.current = test2.current + 1; }}>add</Button>
<Button onClick={logs}>logs</Button>

先点击 add 再点击 logs

{ test: 1 }
{ test2: 1 }

先点击 logs 再点击 add

{ test: 0 }
{ test2: 1 }

Effect Hook

Effect Hook 可以视为 componentDidMountcomponentDidUpdatecomponentWillUnmount 三个生命周期函数的组合。在数据发生变化时执行。

我们可以通过 Effect Hook 对 State 和 props 的变化进行监听。

例子:通过监听list的数据变化,动态变更count。

const [list setlist] = useState({});
const getCount = (list) => { setCount(list.length) };
useEffect(() => {
getCount(list);
}, [list]);

Effect Hook实现生命周期

在函数式组件中,React的原始的生命周期无法使用,我们可以利用Effect Hook的特性模拟生命周期。

  1. 组件初始化 componentDidMount()

    useEffect(() => {
    init();
    }, []);
  2. 组件销毁 componentWillUnmount()

    useEffect(() => {
    return remove;
    }, []);
  3. 组件更新 componentDidUpdate()

    useEffect(() => {
    update();
    });

Memo Hook

计算属性,类似于Vue 的 computed 通过监听某一个State的变化,返回处理后的State。

用法:

const [test, setTest] = useState(0);
const memoValue = useMemo(() => (test + 1), [test]);

AHook

AHook是阿里巴巴团队封装的自定义Hooks工具集。

State Hooks

useReactive

提供一种数据响应式的操作体验,定义数据状态不需要写useState , 直接修改属性即可刷新视图。

const state = useReactive({
count: 0,
inputVal: '',
obj: {
value: '',
},
});

<button onClick={()=>state.count+1}>{state.count}</button>

useBoolean

优雅的管理 boolean 值的 Hook。

const [state, { toggle, setTrue, setFalse }] = useBoolean(true);

生命周期

useMount

只在组件 mount 时执行的 hook。

 useMount(() => {
message.info('mount');
});

useUnmount

只在组件 unmount 时执行的 hook。

useUnmount(() => {
message.info('unmount');
});

useUpdateEffect

一个只在依赖更新时执行的 useEffect hook。使用上与 useEffect 完全相同,只是它忽略了首次渲染,且只在依赖项更新时运行。

useUpdateEffect(() => {
setUpdateEffectCount((c) => c + 1);
return () => {
// do something
};
}, [count]);

useUpdate

强制组件重新渲染的 hook。

const update = useUpdate();

自定义Hooks

useRouter

一个类似 Vue中 useRouter 的封装。其中pushreplace方法会返回一个布尔值,True表示跳转前后路径及Query参数不变。

const router = useRouter();

router.go(-1);
router.back();

const isDuplicated = router.push({
path:'./'
query:{
keyword:''
}
});

const isDuplicated = router.replace({
path:'./'
query:{
    keyword:''
}
})

useRoute

一个类似 Vue中 useRoute 的封装。

// https://react-admin.dxsuite.cn/#/demo/list/1234?pageNo=1
const route = useRoute();
console.log(route.path); // /demo/list/1234
console.log(route.fullPath); // /demo/list/1234?pageNo=1
console.log(route.params); // { id: '1234' }
console.log(route.query); // { pageNo: '1' }

useTimeFormat

一个时间格式化封装

const timeFormat = useTimeFormat();

console.log(timeFormat.YMD('1619769096036')) // '2021-04-30'
console.log(timeFormat.YMDH('1619769096036')) // '2021-04-30 15'
console.log(timeFormat.YMDHm('1619769096036')) // '2021-04-30 15:51'
console.log(timeFormat.YMDHms('1619769096036')) // '2021-04-30 15:51:12'
console.log(timeFormat.ISO('1619769096036')) // '2021-04-30T07:51:36.036Z'
console.log(timeFormat.ISOStartTime('2021-04-30 15:51:12')) // '2021-04-29T16:00:00.000Z'
console.log(timeFormat.ISOEndTime('2021-04-30 15:51:12')) // '2021-04-30T15:59:59.000Z'

useFormLayout

一个表单栅格参数封装

const formLayout = useFormLayout(); // 'default' | 'large' | 'small'

// {
// labelCol: {span: 4},
// wrapperCol: {span: 6},
// },