Hooks使用条件
目前用create-react-app脚手架创建的项目,react和react-dom版本为^16.7.0,该版本还未支持hooks的使用,未升级使用会报错误:TypeError: Object(…) is not a function。
升级react的版本很简单,在该项目目录下执行如下语句即可。
yarn add react@next react-dom@next
完成后可查看package.json文件是否升级成功,该文章代码是基于react react-dom ^16.8.0-alpha.1版本运行的。
Hooks是写在React function里面的,与之前的class不同,就像官网的例子里一样。我们可以这样
const Example = (props) => {
// You can use Hooks here!
return <div />;
}
或者这样
function Example(props) {
// You can use Hooks here!
return <div />;
}
又或者直接
export default (props) => {
// You can use Hooks here!
return <div />;
}
总之
- 只在 React Functions 调用 Hooks
- 只在顶层调用Hook,不能在循环,条件或嵌套函数中调用Hook
useState
首先需要import一下useState
import React, { useState } from "react";
然后我们就可以在function里面使用了,以count为例,方法如下
const [count, setCount] = useState(0);
count是state,0是count的初始值,setCount是设置count的一个函数(函数名不一定要是setXxx,也可以如sCount等,但不建议这么做),setCount()等效于👇
setCount = (count) => {
this.setState({
count
})
}
useState只能在顶层调用,不能在循环,条件或嵌套函数中调用,因为useSate是基于数组的实现的,而他们的下标是根据useState出现顺序决定的,假设判断语句中存在useState,当判断不成立时(即不执行判断包裹的代码时),useState会因为下标错乱而出错。想了解更多可看React hooks: not magic, just arrays (需科学上网)或官网的Rules of Hooks
具体使用如下:
import React, { useState } from 'react';
function Cunter() {
const [count, setCount] = useState(0);
return (
<div className="count-box">
<h1 className="title">点了{count}次</h1>
<button onClick={() => {setCount(count + 1);}}>Click Me!</button>
</div>
);
}
export default Cunter;
useEffect
useEffect结合了componentWillMount、componentDidMount和componentWillUnmount的特性。useEffect可以传入两个参数,第一个是必选参数,为function类型,是effect执行的内容,第二为可选参数,为数组类型,是判断是否执行该effect,若第二参数的数值发生变化,则执行。
useEffect(()=>{
console.log('current count:'+count)
})
👆组件加载时、state变化时,都会执行console.log
useEffect(()=>{
console.log('first effect count:'+count)//new count
return function cleanup(){console.log('first effect return count:'+count)}//old count
})
👆组件加载时执行第一个console.log,state变化时,会先执行return的cleanup函数,打印上一次运行时的count,再执行第一个console.log。
注意!这里上一个运行时的意思是该effect上一次执行时的state,并非是上一个state,这么说有点绕,看一下这个例子👇
useEffect(()=>{
console.log('third effect count:'+count)
return function cleanup(){console.log('third effect return count:'+count)}
},[count>4?1:0])
👆假设你点击了8次,当你点第五次时,此时第二参数的内容从0变成了1,所以执行了该effect,打印了
third effect return count:0
third effect count:5
之后再也不执行该effect,直到将这个组件卸载时,才会执行该effect的cleanup函数,但打印的并不是想象中的 8 ,而是
third effect return count:5
既该effect的上一次运行时的state。
因为该特性,所以并不推荐第二参数传递[]来模拟componentDidMount 和componentWillUnmount,引用官网的原话
While passing [] is closer to the familiar componentDidMount and componentWillUnmount mental model, we suggest not making it a habit because it often leads to bugs, as discussed above. Don’t forget that React defers running useEffect until after the browser has painted, so doing extra work is less of a problem.
完整代码👇
//Count.jsx
import React, { useState, useEffect } from "react";
function Cunter() {
const [count, setCount] = useState(0);
useEffect(()=>{
console.log('first effect count:'+count)
return function cleanup(){console.log('first effect return cleanup')}
},[])
useEffect(()=>{
console.log('second effect, current count:'+count)//new count
return function cleanup(){console.log('second effect return count:'+count)}//old count
})
useEffect(()=>{
console.log('third effect count:'+count)
return function cleanup(){console.log('third effect return count:'+count)}
},[count>4?1:0])
return (
<div className="count-box">
<h1 className="title">点了{count}次</h1>
<button onClick={() => {setCount(count + 1);}}>Click Me!</button>
</div>
);
}
export default Cunter;
//App.jsx
import React, { Component } from "react";
import Cunter from "./Cunter";
class App extends Component {
constructor(props) {
super(props);
this.state = { show: true };
}
render() {
return (
<div>
{this.state.show ? <Cunter /> : <p>cleanup</p>}
<br/>
<button onClick={() => {this.setState({ show: false })}}>cleanup</button>
</div>
);
}
}
export default App;