react hook的出现标志着无状态组件的消失。
从百草园到三味书屋,
从class类组件杀到纯函数组件....
主要从这几个常用hook入手:
useState、useEffect、 useContext、useRef、 useImperativeHandle、memo、useMemo、useCallback
分别例举应用场景。
1、useState
和class的state差不多。
最简单应用:
export default function IndexPage() {
let test = 1;
const [data, setData] = useState(0); //直接赋值
const [data2, setData2] = useState(() => { //可以是一个函数返回值
return test + 1;
});
return (
<div>
<Button onClick={() => setData(1)}>测试{data}</Button>
<Button onClick={() => setData2(2)}>测试{data2}</Button>
</div>
);
}
如果在某些情况不受外界影响,想拿到前一次的值:
export default function IndexPage() {
let test = 1;
const [data, setData] = useState(0); //直接赋值
const [data2, setData2] = useState(() => { //可以是一个函数返回值
return test + 1;
});
const dosome = () => {
setData((prevState => prevState + 1)); //通过这种方式取得最新的上一次值
};
return (
<div>
<Button onClick={() => dosome()}>测试{data}</Button>
<Button onClick={() => setData2(2)}>测试{data2}</Button>
</div>
);
}
2、useEffect
类似 React class 的生命周期函数componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
做请求:
export default function IndexPage() {
const [params, setParams] = useState({ page: 1, pageSize: 10 });
useEffect(() => {
fetch('xxx/xxx/xxx', params).then(r => console.log(r));
}, [params]);
return (
<div>
<Button onClick={() => setParams({ page: 2, pageSize: 10 })}>请求</Button>
</div>
);
}
useEffect:只能在初始渲染和DOM更新后触发
它的第二个参数是一个数组,即依赖,每依赖变动时候,都会触发useEffect内的方法。如果数组为空,那么只会在初始渲染运行一次。
比如当params1或者params2或者params3,任意一个改变的时候就会打印输出1:
useEffect(() => {
console.log(1);
}, [params1, params2, params3]);
假设有多个不同请求时候,需要写多个对应的请求依赖,对应依赖参数变化后做出请求:
export default function IndexPage() {
const [params, setParams] = useState({ page: 1, pageSize: 10 });
const [params2, setParams2] = useState({ page: 1, pageSize: 10 });
useEffect(() => {
fetch1('xxx/xxx/xxx', params).then(r => console.log(r));
}, [params]);
useEffect(() => {
fetch2('xxx/xxx/xxx', params2).then(r => console.log(r));
}, [params2]);
return (
<div>
<Button onClick={() => setParams({ page: 2, pageSize: 10 })}>请求1</Button>
<Button onClick={() => setParams2({ page: 1, pageSize: 5 })}>请求2</Button>
</div>
);
}
当点击请求1,修改params参数变化,触发第一个useEffect;点击请求2,修改params2参数变化,触发第二个useEffect,发起请求以此类推。
卸载时候,如清除定时器:
export default function IndexPage() {
const [params, setParams] = useState({ page: 1, pageSize: 10 });
useEffect(() => {
let time = setInterval(() => {
console.log(111);
}, 1000);
return () => { //这里return一个函数,表示卸载时候运行的方法
clearInterval(time);
};
}, [params]);
return (
<div>
<Button onClick={() => setParams({ page: 2, pageSize: 10 })}>请求1</Button>
</div>
);
}
3、useContext
穿透传递上下文数据或者方法,在父组件创建一个上下文,再通过Provider传递给子组件,只需要包裹住,不管子组件多少层级都可以随意用到。
-------父组件:
import React, { useState, createContext } from 'react';
import { Button } from 'antd';
import Child from './Child';
export const TestContext = createContext('默认参数');
export default function IndexPage() {
const [params, setParams] = useState('');
return (
<TestContext.Provider value={params}>
<Button onClick={() => setParams('参数2')}>点击</Button>
<div>父组件</div>
<Child />
</TestContext.Provider>
);
}
-------子组件:这里子组件引用孙组件
import React from 'react';
import ChildTwo from '../ChildTwo';
function Index() {
return (
<div>
<div>
孩子组件
</div>
<ChildTwo />
</div>
);
}
export default Index;
-------孙组件:最终取值
import React, { Component, useContext } from 'react';
import { TestContext } from '../index';
function Index() {
let getContext = useContext(TestContext);
return (
<div>
孙子组件: {getContext}
</div>
);
}
export default Index;
点击前:
点击后:
4、useImperativeHandle(需要配合forwardRef、useRef)
作用:hook中父组件可以直接调用子组件的方法
正常下,我们编写代码,都是把父组件方法通过props传递给子组件运行,某些情况下,需要把子组件方法传递给父组件,hook组件需要这样写,父组件使用useRef并且赋给子组件:
请注意:hooks中要使用useRef,而class组件要使用createRef。
-----父组件
import React, { useRef } from 'react';
import { Button } from 'antd';
import Child from './Child';
export default function IndexPage() {
const childRef = useRef();
const clickChild = () => {
childRef.current.change();
};
return (
<div>
<Button onClick={() => clickChild()}>点击</Button>
<div>父组件</div>
<Child ref={childRef} />
</div>
);
}
------子组件
useImperativeHandle的第一个参数接收到的ref,第二个是一个函数,函数需要返回传递给父组件的方法,子组件用forwardRef包裹,类似高阶函数用法。
import React, { useImperativeHandle, useState, forwardRef } from 'react';
function Index(props, ref) {
const [data, setData] = useState(1);
const test = () => {
setData(data + 1);
};
useImperativeHandle(ref, () => ({
change: () => test(),
}));
return (
<div>
<div>
孩子组件:{data}
</div>
</div>
);
}
export default forwardRef(Index);
每次点击按钮数值加1
5、memo
效果等同与class组件的PureComponent,或者是shouldCompomentUpdate,只能进行浅层比较,既简单数据类型如:bool、string、number、undefind、null
作用:避免组件做不必要的更新(减少频繁更新次数)
使用非常简单,用包裹于子组件。一般情况下也是用来控制,当父组件传给子组件的值变化时候,子组件避免做不必要频繁更新次数。
import React, { memo } from 'react';
function Index() {
return (
<div>
</div>
);
}
export default memo(Index);
网友评论