hooks是react16.8新增的特性。关于为何要新增hooks,是由于class的组件会存在如下的一些问题。javascript
这些点就不详细赘述了,这篇文章的重点是介绍hooks。html
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
前端
useState是用来取代class组件中的setState。useState接受一个值做为state的初始值,返回一个数组,数组第一个值是state,第二个值是一个用于修改state的函数。useState能够屡次使用。栗子以下:vue
import React, { useState } from 'react'; const App = function() { const [count, setCount] = useState(0); // 另外一个没有使用的state const [other, setOther] = useState('hello'); return ( <button onClick={() => setCount(count+1)}>{ count }</button> ); }
它对应的class组件的代码以下:java
import React, { Component } from 'react'; class App extends Component { constructor(props) { super(props); this.state = { count: 0, other: 'hello' }; } setCount(count: number) { this.setState({ count }); } setOther(other: string) { this.setState({ other }); } render() { return ( <button onClick={() => this.setCount(this.state.count + 1)}> {this.state.count} </button> ); } }
useReducer和useState的用处是同样的,不一样的是useReducer处理的是更加复杂的场景,例如三级联动选择框,须要同时对多个state进行联动处理。能够看作是一个小的redux,使用方式和redux也基本一致。react
const reducer = (state, action) => { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: return state; } } const App = function() { const [state, dispatch] = useReducer(reducer, { count: 0 }); return ( <> { state.count } <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); }
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
type EffectCallback = () => (void | (() => void | undefined));
redux
Effect Hook 可让你在函数组件中执行反作用操做。何为反作用操做呢?例如:请求接口、操做dom、定时器等等。咱们可使用useEffect模拟React中的一些生命周期函数。但须要注意的是:请不要把这种对应划上等号。
api
从他的定义中就能够知道,useEffect有两个参数,第一个参数是一个回调函数(能够返回一个函数或者不返回内容),第二个参数是依赖项。数组
模拟componentDidMount:dom
// 在didMount中请求一个接口 useEffect(() => { fetch('http://xxx.com/api/list'); }, []);
模拟componentDidUpdate
// 不传入依赖时,会在每次渲染以后执行 useEffect(() => { console.log('update'); });
模拟componentWillUnmount: useEffect的回调函数返回一个函数。
// 向document添加一个click事件,并在下次从新渲染前将其卸载。 useEffect(() => { const handler = () => { console.log('click'); } document.addEventListener('click', handler); return () => { document.removeEventListener('click', handler); } }, []);
模拟shouldComponentUpdate: 这个我以为应该和vue的watch更加相近。
// 在第一个加载和count发生变化时才会触发 useEffect(() => { // ... }, [count]);
若是你对vue比较熟悉的话,useMemo能够看作是computed
,能够看作是须要手动添加依赖的计算属性,在依赖的值不发生改变时,返回的值是不变的。
const App = function() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const name = useMemo(() => { return firstName + ' ' + lastName }, [firstName, lastName]); return ( <> 姓:<input value={firstName} onChange={(e) => setFirstName(e.target.value)} /> 名:<input value={lastName} onChange={(e) => setLastName(e.target.value)} /> 姓名:{ name } </> ); }
咱们知道,函数组件中props或者state改变时,都会从新执行当前函数,那么若是咱们直接在函数组件中定义处理函数的话,属性更改会触发修改,那么每次修改都会致使处理函数的从新定义,这样会形成极大的性能耗损。
useCallback即是用来处理这个问题,在依赖项不改变的状况下,函数不会从新定义。
const App = function() { const change = useCallback((e) => { console.log(e); }, []); return ( <> <input onChange={change} /> </> ); }
ref的做用我想不少前端小伙伴应该并不陌生,能够用来操做dom或者实例。而useRef除了用于操做DOM以外,在一些其余的方面也颇有用,例如咱们须要把一个定时器的值全局保存,但又不但愿这个值的变化触发render,就像是咱们在使用class组件时的this.timer = setInterval(...)
。
// 例如咱们设置了定时获取数据的interval,在点击某个按钮以后就中止定时器 const App = () => { const inputEl = useRef(null); // 这个用来获取dom,绑定到input上以后,就能够经过inputEl.current进行访问 const timer = useRef(null); useEffect(() => { timer.current = setInterval(...); return () => { clearInterval(timer.current); } }, []); return ( <div> <button onClick={() => clearInterval(timer.current)}>Stop</button> <input ref={inputEl} /> </div> ); }
从字面上也能够看出来,useContext就是为了方便使用context(通常用于祖孙组件的数据通讯
)的。须要注意的是调用了 useContext 的组件总会在 context 值变化时从新渲染。
const themes = { light: { color: '#fff', background: '#f12' } }; const ThemeContext = createContext(themes.light); const Child = () => { const theme = useContext(ThemeContext); return ( <div style={{color: theme.color, background: theme.background}}>Child</div> ); } const App = () => { return ( <div> <ThemeContext.Provider value={themes.light}> <Child /> </ThemeContext.Provider> </div> ); }
自定义hook通常用来HOC作比较,他们都是用来对组件的逻辑进行复用。hook与HOC不一样的是:
自定义hooks或使用hooks时须要注意的是:
use
开头,这有利于react对自定义hook的规则进行检查;栗子:在多个组件中须要实时获取鼠标的位置
// 定义hook const useMousePosition = () => { const [location, setLocation] = useState({x: 0, y: 0}); useEffect(() => { function move(e) { setLocation({ x: e.screenX, y: e.screenY }); } document.addEventListener('mousemove', move); return () => { document.removeEventListener('mousemove', move); }; }, []); return location; } // 使用hook const App = () => { const position = useMousePosition(); return (<div>{ position.x } { position.y }</div>); }
能够看到,咱们的就对代码进行了复用,也避免了上述关于HOC的问题。
hooks的引入让咱们很方便的使用函数组件来编写代码,不过关于OOP和FP的争议也一直存在,因此是否使用hooks也须要童鞋们好好考量。