一、组件类的缺点
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
这个例子用来显示一个计数器。当你点击按钮,计数器的值就会增加:
import React, { useState } from 'react';
function Example() {
// 声明一个新的叫做 “count” 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
官方解读:
1: 引入 React 中的 useState Hook。它让我们在函数组件中存储内部 state。
2: 在 Example 组件内部,我们通过调用 useState Hook 声明了一个新的 state 变量。它返回一对值给到我们命名的变量上。我们把变量命名为 count,因为它存储的是点击次数。我们通过传 0 作为 useState 唯一的参数来将其初始化为 0。第二个返回的值本身就是一个函数。它让我们可以更新 count 的值,所以我们叫它 setCount。
3: 当用户点击按钮后,我们传递一个新的值给 setCount。React 会重新渲染 Example 组件,并把最新的 count 传给它。
import React, { Component } from "react";
export default class Button extends Component {
constructor() {
super();
this.state = { buttonText: "Click me, please" };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(() => {
return { buttonText: "Thanks, been clicked!" };
});
}
render() {
const { buttonText } = this.state;
return <button onClick={this.handleClick}>{buttonText}</button>;
}
}
这个组件类仅仅是一个按钮,但可以看到,它的代码已经很"重"了。真实的 React App 由多个类按照层级,一层层构成,复杂度成倍增长。再加入 Redux,就变得更复杂。
- 大型组件很难拆分和重构,也很难测试。
- 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。
- 组件类引入了复杂的编程模式,比如 render props 和高阶组件。
二、函数组件
React 团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。 组件的最佳写法应该是函数,而不是类。
不过之前的版本中函数定义组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
但是,这种写法有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类。React Hooks 的设计目的,就是加强版函数组件,完全不使用"类",就能写出一个全功能的组件。也就是函数组件可以像类组件一样使用。
纯函数
三、Hook
Hook 这个单词的意思是"钩子"。
Hook 是什么? Hook 是一个特殊的函数,它可以让你“钩入” React 的特性
Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。(还有一个地方可以调用 Hook —— 就是自定义的 Hook )
- useState()
- useContext()
- useReducer()
- useEffect()
四 常用hooks
1. useState 状态钩子
useState()这个函数接受状态的初始值,作为参数,该函数返回一个数组,数组的第一个成员是一个变量,指向状态的当前值。第二个成员是一个函数,用来更新状态,约定是set前缀加上状态的变量名。这个初始 state 参数只有在第一次渲染时会被用到tips
- setState的值需要是新值(新的地址)
一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留。
setObj(Object.assign(obj, { name: 'lisi' })) setObj({...obj, name: 'lisi}) 会发现值没有变 因为地址没变 assign只是在原来地址合并。
setState的值需要是新值,可以接受一个函数作为参数 函数的返回值需要是新值
import React, { useState } from 'react'; const App = () => { const [arr, setArr] = useState([1,2,3]); return ( <div> <button onClick={() => setArr(arg => { arr.push(4) console.log(arr === arr); // return arr 这样不改变 需要新的arr return [...arr] })}> Click me </button> <h2>{arr}</h2> </div> ); } export default App;
useState值为函数时,函数的返回值为初始值。
import React, { useState } from 'react';
const App = () => {
const [func, setFunc] = useState(()=>({name: 'lisi'}));
return (
<div>
<button onClick={() => setFunc({name: 'pp'})}>
change-name
</button>
{/* Objects are not valid as a React child */}
{/* <h2>{func}</h2> */}
<h2>{func.name}</h2>
</div>
);
}
export default App;
2. useEffect 副作用钩子
useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的用途
语法:
useEffect(() => {
// Async Action
}, [dependencies])
useEffect()接受两个参数。第一个参数是一个函数,异步操作的代码放在里面。第二个参数是一个数组,用于给出 Effect 的依赖项,只要这个数组发生变化,useEffect()就会执行。第二个参数可以省略,这时每次组件渲染时,就会执行useEffect()。副作用函数还可以通过返回一个函数来指定如何“清除”副作用。
解答:
参数2不写,默认是监视所有状态,只要有状态改变都会执行
参数2为空数组, 不监视任何状态,谁变化也不执行,但是第一次渲染是会执行的
参数2填写,监听改状态
例子:
tips
- 副作用函数需要在组件内声明,它们可以访问到组件的 props 和 state(闭包)。
- 无阻塞更新
获取数据可以在组件挂载前后获取,而effect是挂载后获取,这就表明数据就算请求出错,但是组件已经挂载结束,数据请求失败不会影响界面。
- 可以有多个useEffect
3. useReducer action 钩子
- React 本身不提供状态管理功能,通常需要使用外部库。这方面最常用的库是 Redux。
- Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态,Reducer 函数的形式是(state, action) => newState。
useReducers()钩子用来引入 Reducer 功能。
语法
它接受 Reducer 函数和状态的初始值作为参数,返回一个数组。数组的第一个成员是状态的当前值,第二个成员是发送 action 的dispatch函数。