这里写使用自定义目录标题
使用useEffect在函数组件中启用定时器
function UseReducerTemplate(props){
const [count, setCount] = useState(0);
useEffect(() => {
let id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
};
由于需要启用定时器,而且防止变量刷新,需要将useEffect中的第二个参数设置为空数组,防止重复渲染生成计数器,以及清除计数器。此时在函数第一次render()的时候创建定时器。相当于class组件中的componentDidMount生命周期创建定时器。
存在问题:此时进行渲染后发现页面中当0变成1后页面保持不变,并且一直是1。
原因:当第二个参数为空时此时第一个函数中形成了闭包,此时count值成为了一个常量0,而读取不到外部count值的变化,此时第一个函数是一个独立函数,其内部的状态和外部无关
存在问题的修复方式
function UseReducerTemplate(props){
const [count, setCount] = useState(0);
useEffect(() => {
let id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, [count]);
return <h1>{count}</h1>;
};
如果此时函数想要正常读取外部的count则需要将useState中count值传入第二个参数,但是此时会出现刚开始提到的问题,计数器在重复的挂载清除,会存在比较大的性能损耗,因此此种方式也是不可取的
在hooks使用规范上有提示到,hooks内部使用的到状态参数要传入到第二个参数数组中,即effect 函数中引用的每个值也应出现在依赖项数组中。
可用的修复方式1
function UseReducerTemplate(props){
const [count, setCount] = useState(0);
useEffect(() => {
let id = setInterval(() => {
setCount(count => count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
};
由于setState()中还可以接受一个函数,setState(preState => { return newState }), 通过此种方式可以读取到上一次的state值,并在上一值的基础上进行更改, 函数返回方式在维护一个state对象时有较好的使用效果,例如:setUser((user) = > ({ …user, name: ‘Nathan’ }));
可用的修复方式2
function UseReducerTemplate(props){
const [count, setCount] = useState(0);
const countRef = useRef(count);
useEffect(() =>{
countRef.current = count;
});
useEffect(() => {
let id = setInterval(() => {
setCount(countRef.current + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
};
由于此处产生了闭包,导致函数内部count成为了一个常量,因此我们创建一个count值的引用,此处countRef是维护一个对象,由于hooks在函数中是顺序调用,因此每次count值修改后其内部的curent会更改为当前的count值,因此我们在setInterval中可以正确进行读取