hooks + 函数组件
实质: 使用函数组件+hook 来实现 类组件的功能
官方期望用函数组件+hook 逐步代替类组件
为什么要代替类组件?
- 降低 react 的入门门槛,增加开发者数量
- 降低开发难度
- 函数式编程
函数组件
- 函数组件就是一个函数
- 函数组件由两个参数\
- props 就是绑定在 App 组件身上的 props
- ref ref 绑定,用于获取内容
- 函数组件特点
- this 为 undefined
- 如果只使用函数组件,那么 react 的很多特性都不可以使用了,比如: state \ setState \ forceUpdate \contextType \ 生命周期 。。。
1 2 3 4 5
| interface P {} const App = (props: P, ref: any) => { return <div> App </div>; }; export default App;
|
Hook 钩子函数
- react 16.8 版本之后才提供了一个新的特性:Hook
- Hook + 函数组件才能实现 react 的特性
Hook
官方提供了 10 个内置 Hook
useState(initValue) // initValue 初始值
用于函数组件定义 state,和修改 state 的方法
定义格式: const [stateName,setStateName] = useState<数据类型>( 初始值 )
比如: const [name,setName] = useState(‘lakers’)
注意点
基础数据类型: 直接赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import React, { useState } from "react";
export default function App() { const [name, setName] = useState<string>("yyc"); return ( <div> <button onClick={() => { setName("yyc2"); }} > 修改名字 </button> <p>{name}</p> </div> ); }
|
引用数据类型,解构赋值给它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import React, { useState } from "react";
interface People { name: string; }
export default function App() { const [people, setPeople] = useState<People>({ name: "fox", });
const [arr, setArr] = useState<unknown[]>([1, 2, 3, 4]); const changeName = () => {
setPeople({ name: "dog", });
};
const add = () => { arr.push(arr.length + 1); setArr([...arr]); }; return ( <div> <button onClick={changeName}>修改名字</button> <p>{people.name}</p> <button onClick={add}>增加</button> <ul> {arr.map((item, index) => ( <li key={index}> {item} </li> ))} </ul> </div> ); }
|
useEffect
useEffect 可以让函数组件实现类组件部分生命周期的功能
可以实现以下几个生命周期钩子函数的功能
- componentDidMount
- componentDidUpdate [ 属性或者状态的监听 ]
- componentWillUnmount
- useEffect(回调函数,依赖项)
- 有返回值,而且返回值是一个函数,—–> 相当于 componentWillUnmount
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import React from "react"; import Hello from "./Hello";
export default function App() { const [flag, setFlag] = React.useState<boolean>(true); return ( <div> <button onClick={() => { setFlag(false); }} > {" "} 销毁{" "} </button> {flag && <Hello />} </div> ); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { time } from "console"; import React, { useEffect } from "react";
export default function Hello() { let timer: any = null; useEffect(() => { console.log("创建了"); timer = setInterval(() => { console.log("1111"); }, 1000); }, []); useEffect(() => { return () => { console.log("销毁了"); clearInterval(timer); }; }); return <div>hello</div>; }
|
- 依赖项就是一个数组
- 可以是空数组 —–> 相当于 componentDidMount
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import React, { useEffect } from "react";
export default function App() { useEffect(() => { console.log("mount"); const pTxt: any = document.querySelector("p"); pTxt.style.background = "pink"; }, []); return ( <div> <p>123</p> </div> ); }
|
- 数组中也可以放 state 或者 props ——> componentDidUpdate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import React, { useEffect, useState } from "react";
export default function App() { const [count, setCount] = useState<number>(0); useEffect(() => { console.log("执行"); }, [count]); return ( <div> <button onClick={() => { setCount((pre) => pre + 1); }} > 按钮 </button> <p>{count}</p> </div> ); }
|
useContext
- 新建 context 文件夹/index.ts 文件
1 2 3 4 5 6
| import React, { createContext } from "react";
const ctx = createContext(0);
export default ctx;
|
- 用 ctx.Provider 包裹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React, { useState } from "react"; import Father from "./components/Father"; import ctx from "./context";
export default function App() { const [money] = useState<number>(200000); return ( <div> <h3> useContext -- 跨组件通信 </h3> <ctx.Provider value={money}> <Father /> </ctx.Provider> </div> ); }
|
- 子组件用 useContext 来接收
1 2 3 4 5 6 7
| import React, { useContext } from "react"; import ctx from "../context";
export default function GrandSon() { const money = useContext(ctx); return <div>{money}</div>; }
|
useRef + useImperativeHandle
- 可以绑定元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React, { useRef } from "react";
export default function App() { const pRef: any = useRef(null); const setBgc = () => { pRef.current.style.background = "pink"; }; return ( <div> <button onClick={setBgc}>按钮</button> <p ref={pRef}>123</p> </div> ); }
|
- 可以绑定类组件(计数案例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React, { useRef } from "react"; import Hello from "./Hello";
export default function App() { const helloRef: any = useRef(null); const addVal = () => { helloRef.current.add(); }; return ( <div> <button onClick={addVal}>+</button> <Hello ref={helloRef} /> </div> ); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import React, { Component } from "react";
interface P {}
interface S { count: number; } export default class Hello extends Component<P, S> { constructor(props: P) { super(props); this.state = { count: 0, }; } add = () => { this.setState({ count: this.state.count + 1, }); }; render() { const { count } = this.state; return ( <div> <p> {count} </p> </div> ); } }
|
- 不可以绑定函数组件(解决:使用 React.forwardRef + useImperativeHandle)
- useImperativeHandle(ref,()=>{})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React, { useRef } from "react"; import Hello from "./Hello";
export default function App() { const helloRef: any = useRef(null); const addVal = () => { helloRef.current.add(); }; return ( <div> <button onClick={addVal}>+</button> <Hello ref={helloRef} /> </div> ); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import React, { useState, useImperativeHandle } from "react";
function Hello(props: any, ref: any) { const [count, setCount] = useState<number>(0); const add = () => { setCount((pre) => { return pre + 1; }); }; useImperativeHandle( ref, () => { return { add, }; } ); return ( <div> <p>{count}</p> </div> ); }
export default React.forwardRef(Hello);
|
useReducer
- 用于定义状态,修改状态,和 useState 相似
1 2 3
| const [state, dispatch] = useReducer(reducer, initialState);
|
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import React, { useReducer } from "react";
const initialState = { count: 0, };
const reducer = (state: any, action: any) => { switch (action.type) { case "in": return { count: state.count + 1 }; case "de": return { count: state.count - 1 }; default: throw new Error(); } };
export default function App() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <button onClick={() => { dispatch({ type: "in", }); }} > + </button> <hr /> <button onClick={() => { dispatch({ type: "de", }); }} > - </button> <p>{state.count}</p> </div> ); }
|
hooks 中用于渲染优化的(useMemo/useCallback)
- useMemo(返回的是一个记忆值,直接用)
- 参数(callback,array) 第一个为函数方法,第二个为数组 填依赖项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import React, { useState, useMemo } from "react";
export default function App() { const [count, setCount] = useState<number>(0); const [list] = useState<number[]>([1, 2, 3, 4]); const add = () => { setCount((pre) => pre + 1); }; const renderList = () => { console.log("chufale"); return list.map((item, index) => <li key={index}> {item} </li>); }; const memoList = useMemo(renderList, [list]); return ( <div> <button onClick={add}>+</button> <p>{count}</p> {} 这里出现的问题 点击增加会重新渲染list列表 <br /> 解决办法:useMemo {memoList} </div> ); }
|
- useCallback(callback,array) 用法和 useMemo 一样
- useCallback 返回值是一个记忆函数
- 这个记忆函数必须传递给子组件用
- 这个子组件必须是 React.memo() 的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import React, { useState, useCallback } from "react"; import Hello from "./Hello";
export default function App() { const [count, setCount] = useState<number>(0); const [list] = useState<number[]>([1, 2, 3, 4]); const add = () => { setCount((pre) => pre + 1); }; const renderList = () => { console.log("chufale"); return list.map((item, index) => <li key={index}> {item} </li>); };
const callbackList = useCallback(renderList, [list]); return ( <div> <button onClick={add}>+</button> <p>{count}</p> {} 这里出现的问题 点击增加会重新渲染list列表 <br /> 解决办法:useCallback <Hello callbackList={callbackList} /> </div> ); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React, { memo, ReactNode } from "react";
interface P { callbackList: () => ReactNode; } function Hello(props: P) { return ( <div> <ul>{props.callbackList()}</ul> </div> ); }
export default memo(Hello);
|
useLayoutEffect 用法和 useEffect 一致
useDebugValue
自定义 HOOK 封装
- 封装一个函数,只不过这个函数有些特别【useState,useEffect】
- 自定义 Hook 名称
- 其他 hooks 封装: