本文章视频地址:视频链接
一、React组件分类
二、Hook函数概览
Hook 是 React 16.8 的新增特性!并且只能运用到函数组件中!
1.useState
作用:在函数组件中使用状态,修改状态值可让函数组件更新,类似于类组件中的setState
语法: const [state,setState] = useState(initialState); 返回一个 state,以及更新 state 的函数
import React, { useState } from "react";
export default function Demo(props) {
let [num, setNum] = useState(10);
const handler = () => {
setNum(num + 1);
};
return <div>
<span>{num}</span>
<button onClick={handler}>新增</button>
</div>;
};
2.useEffect
作用:在函数组件中使用生命周期函数
语法:具备很多情况 useEffect([callback],[dependencies])
import React, { useState, useEffect } from "react";
export default function Demo() {
let [num, setNum] = useState(10),
[x, setX] = useState(100);
/*
在组件 第一次渲染完 && 每一次更新完 调用
等同于 componentDidMount && componentDidUpdate
*/
useEffect(() => {
console.log('@1', num, x);
});
/*
只在组件 第一次渲染完 调用
等同于 componentDidMount
*/
useEffect(() => {
console.log('@2', num, x);
}, []);
/*
第一次渲染完 以及 依赖的状态发生改变 时调用
*/
useEffect(() => {
console.log('@3', num);
}, [num]);
/*
返回的函数将在 组件卸载后 被调用
等同于 componentWillUnmount
*/
useEffect(() => {
return () => {
console.log('@4');
};
}, []);
return <div>
<span>{num}</span>
<button onClick={() => { setNum(num + 1); }}>处理</button>
<br />
<span>{x}</span>
<button onClick={() => { setX(x + 1); }}>处理</button>
</div>;
};
【注意点1:】只能在函数最外层调用 Hook,不要在循环、条件判断或者子函数中调用。
import React, { useState, useEffect } from "react";
export default function Demo() {
let [num, setNum] = useState(10);
if (num >= 10) {
// Error:React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
useEffect(() => {
console.log('@1', num);
});
}
return <div>
<span>{num}</span>
<button onClick={() => { setNum(num + 1); }}>处理</button>
</div>;
};
【注意点2:】异步获取数据
不能直接对[callback]设置async,因为它只能返回一个函数(或者不设置返回值)
import React, { useState, useEffect } from "react";
const queryData = () => {
return fetch('/api/subscriptions/recommended_collections')
.then(response => {
return response.json();
});
};
export default function Demo() {
let [data, setData] = useState([]);
/* // Warning: useEffect must not return anything besides a function, which is used for clean-up.
useEffect(async () => {
let result = await queryData();
setData(result);
console.log(result);
}, []); */
useEffect(() => {
const next = async () => {
let result = await queryData();
setData(result);
console.log(result);
};
next();
}, []);
return <div>
...
</div>;
};
3.useRef
简单来说,useRef就像一个储物箱,你可以随意存放任何东西,再次渲染时它会去储物箱找,createRef每次渲染都会返回一个新的引用,而useRef每次都会返回相同的引用。
import React, { useState, useEffect, useRef, createRef } from "react";
let prev;
export default function Demo() {
const [num, setNum] = useState(0);
const btnBox = useRef(null); //换成createRef也是可以的
if (!prev) {
prev = btnBox;
} else {
console.log(prev === btnBox);
}
useEffect(() => {
console.log(btnBox.current);
}, []);
return <div>
<span>{num}</span>
<button ref={btnBox} onClick={() => setNum(num + 1)}>按钮</button>
</div>;
};
4.useContext
作用:跨组件共享数据
说明:在没有hook之前,我们通常都会通过 xxxContext.Provider 和 xxxContext.Consumer 的方式来传递和获取context的值,使用hook之后,传递context的方式不变,但子元素获取context的方式变得更加的简洁。
// 以前的定义方式
const CountContext = React.createContext()
<CountContext.Provider value={{ count: 10 }}>
<...自定义的组件>
</CountContext.Provider>
// 子元素
<CountContext.Consumer>
{ value => { console.log(value.count) }} //10
</CountContext.Consumer>
//使用context的获取方式
const countObj = useContext(CountContext)
console.log(countObj.count) // 10
5.useReducer
作用:useReducer相当于是useState的升级版,与useState类似,都是用来保存状态,但它的不同点在于可以定义一个reducer的纯函数,来处理复杂数据。
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
6.useCallback
作用:函数式组件中,每一次更新状态,自定义的函数都要进行重新的声明和定义,如果函数作为props传递给子组件,会造成子组件不必要的重新渲染,有时候子组件并没有使用到父组件发生变化的状态,此时可以使用useCallback来进行性能优化,它会为函数返回一个记忆的值,如果依赖的状态没有发生变化,那么则不会重新创建该函数,也就不会造成子组件不必要的重新渲染。
父组件:
import React, { useState, useCallback, memo } from "react";
export default function Demo() {
let [val, setVal] = useState('');
const handler1 = () => {
setVal('哈哈哈');
};
const handler2 = useCallback(() => {
setVal('呵呵呵');
}, []);
return <div>
<input type="text" value={val}
onChange={ev => {
setVal(ev.target.value);
}} />
<Child1 handler={handler1} />
<Child2 handler={handler2} />
</div>;
};
子组件Child1:
class Child1 extends React.PureComponent {
render() {
console.log('child1 render');
return <div>
<button onClick={this.props.handler}>处理1</button>
</div>;
}
}
子组件Child2:
// 如果子组件是函数组件,则需要useCallback和memo配合使用
const Child2 = memo(function Child2({ handler }) {
console.log('child2 render');
return <div>
<button onClick={handler}>处理2</button>
</div>;
});
7.useMemo
作用:useMemo也是返回一个记忆的值,如果依赖的内容没有发生改变的话,这个值不会发生变化。
注意:useMemo与useCallback的不同点在于useMemo需要在传入的函数里需要return 一个值。
import React, { useState, useMemo } from "react";
export default function Demo() {
let [x, setX] = useState(10),
const cacheVal = useMemo(() => {
// 大量的计算...
return x;
}, [x]);
return <div>
<span>{cacheVal}</span>
<button onClick={() => setX(x + 1)}>处理</button>
</div>;
};
8.自定义Hook
使用自定义hook可以将某些组件逻辑提取到可重用的函数中
import React, { useState } from "react";
const usePartState = function usePartState(initial) {
let [state, setState] = useState(initial);
const setPartState = (partState) => {
setState({
...state,
...partState
});
};
return [state, setPartState];
};
export default function Demo() {
let [state, setState] = usePartState({
x: 10,
y: 20
});
return <div>
<span>{state.x}</span>
<span>{state.y}</span>
<button onClick={() => {
setState({
x: state.x + 1
});
}}>处理x</button>
</div>;
};