React18之前的HOOK
useContext
作用: 在接收context传递数据的组件中,使用useContext快速拿到数据
-- context.js import React from 'react' export default React.createContext() -- App.js export default class App extends Component { render() { return ( <testContext.Provider value={{ name: 'zs' }}> <Test></Test> </testContext.Provider> ) } } -- Test.js import React, { useContext } from 'react' import testContext from './context' export default function Test() { const context = useContext(testContext) console.log(context) return <div></div> }
useReducer
组件中有大量数据,使用useState,略显繁琐,使用useReducer更方便的管理数据
import React, { useReducer } from 'react' const initialState = { count: 0, msg: '哈哈' } function reducer(state, action) { switch (action.type) { case 'increment': return { ...state, count: state.count + 1, } case 'decrement': return { ...state, count: state.count - 1, } default: throw new Error() } } export default function Test1() { const [state, dispatch] = useReducer(reducer, initialState) return ( <> Count: {state.count} msg: {state.msg} <button onClick={() => dispatch({ type: 'decrement' })}>-</button> <button onClick={() => dispatch({ type: 'increment' })}>+</button> </> ) } // useReduder的第三个参数: function init(initialCount) { return {count: initialCount}; } function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; case 'reset': return init(action.payload); default: throw new Error(); } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialCount, init); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
useCallBack
每次函数组件更新,函数组件内的函数会重新创建.使用useCallBack可以缓存函数
import React, { useState, useCallback } from 'react' export default function Test() { console.log('test渲染了') const [count, setCount] = useState(0) // 如果是空数组则回调函数只创建一次.如果不写第二个参数,或第二个参数监听数据,回调函数则创建多次 const handle = useCallback(() => { console.log(count) setCount(count + 1) }, [count]) return ( <div> <div>{count}</div> <button onClick={handle}>按钮</button> </div> ) }
React.memo
作用类似于类组件的PureComponent, 当父组件更新时,动态判断子组件使用更新
--App.js import React, { useState, useEffect } from 'react' import Test from './Test' function getRandomIntInclusive(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值 } class App extends React.Component { state = { count: 0, } render() { console.log('app渲染了') return ( <div> <button onClick={() => { this.setState({ count: getRandomIntInclusive(1, 2), }) }} > 按钮 </button> <Test count={this.state.count}></Test> </div> ) } } export default App -- Test.js import React from 'react' function Test(props) { console.log('Test组件渲染了-' + props.count) return <div>{props.count}</div> } export default React.memo(Test)
useMemo
会缓存一个计算的结果,如果没有变化,则不会重新执行计算
// 使用前 import React,{useState} from 'react'; export default function WithoutMemo() { const [count, setCount] = useState(1); const [val, setValue] = useState(''); function expensive() { console.log('compute'); let sum = 0; for (let i = 0; i < count * 100; i++) { sum += i; } return sum; } return <div> <h4>{count}-{val}-{expensive()}</h4> <div> <button onClick={() => setCount(count + 1)}>+c1</button> <input value={val} onChange={event => setValue(event.target.value)}/> </div> </div>; } // 使用后 export default function WithMemo() { const [count, setCount] = useState(1); const [val, setValue] = useState(''); const expensive = useMemo(() => { console.log('compute'); let sum = 0; for (let i = 0; i < count * 100; i++) { sum += i; } return sum; }, [count]); return <div> <h4>{count}-{expensive}</h4> {val} <div> <button onClick={() => setCount(count + 1)}>+c1</button> <input value={val} onChange={event => setValue(event.target.value)}/> </div> </div>; }
useRef
新的创建ref对象的方式
function TextInputWithFocusButton() { const inputEl = useRef(); // 传入的null表示inputEl.current的初始值 const onButtonClick = () => { // `current` 指向已挂载到 DOM 上的文本输入元素 inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
useImperativeHandle
useImperativeHandle
可以让你在使用ref
时自定义暴露给父组件的实例值.useImperativeHandle
应当与 [forwardRef
]一起使用作用: 封装公共组件的时候,可以给使用公共组件的组件提供指定的API. 有条件的操作公共组件的真实dom
--App.js import React, { useState, useEffect } from 'react' import FancyInput from './FancyInput' const inputRef = React.createRef() class App extends React.Component { render() { return ( <div> <button onClick={() => { console.log(inputRef.current.focus()) }} > 按钮 </button> <FancyInput ref={inputRef}></FancyInput> </div> ) } } export default App --FancyInput.js import React, { useRef, useImperativeHandle, forwardRef } from 'react' function FancyInput(props, ref) { const inputRef = useRef() useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus() }, })) return <input ref={inputRef} /> } FancyInput = forwardRef(FancyInput) export default FancyInput
useLayoutEffect
作用与
useEffect
相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect
内部的更新计划将被同步刷新。
useEffect 这个是在render结束后,你的callback函数执行,但是不会阻塞浏览器渲染 useLayoutEffect 这个是用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制 import React, { useEffect, useLayoutEffect, useRef } from 'react' import TweenMax from 'gsap' // npm i gsap@3.7.0 import './index.css' const Animate = () => { const REl = useRef(null) useLayoutEffect(() => { /*下面这段代码的意思是当组件加载完成后,在0秒的时间内,将方块的横坐标位置移到600px的位置*/ TweenMax.to(REl.current, 0, { x: 600 }) }, []) return ( <div className="animate"> <div ref={REl} className="square"> square </div> </div> ) } export default Animate
useDebugValue
useDebugValue 可用于在 React 开发者工具中显示 自定义 hook 的标签
注意:只能在自定义hook中使用
import { useState, useDebugValue } from 'react' function getRandomIntInclusive(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值 } export default function useFriendStatus() { const [isOnline, setIsOnline] = useState(null) setTimeout(() => { let result = getRandomIntInclusive(0, 1) console.log(result) setIsOnline(result ? 'online' : 'offline') }, 1000) // 在react开发者工具中的这个 Hook 旁边显示标签 // "FriendStatus: Online" useDebugValue(isOnline) return isOnline }
React18新增的HOOK
useId
用于生成唯一值
import React, { useId } from 'react' export default function Checkbox() { const id = useId() return ( <> <label htmlFor={id}>Do you like React?</label> <input id={id} type="checkbox" name="react" /> </> ) }
useTransition
useTransition
可以将一些状态更新标记为不紧急。默认情况下,所有状态更新都是紧急的。React会对所有紧急状态更新的视图进行并行渲染. 如果渲染任务比较重,页面渲染看起来会有些卡顿. React 将允许紧急状态更新(例如,更新文本输入)以中断非紧急状态更新(例如,呈现搜索结果列表)。
import {startTransition, useTransition} from 'react'
以搜索github用户为例: 输入关键字则立刻检索相关用户信息并渲染
App.js
import React, { useState } from 'react' import Card from './Card' export default function App() { const [keyWord, setKeyWord] = useState('') return ( <div> 请输入关键字: <input type="text" value={keyWord} onChange={(e) => { // react18 以前,所有的更新都是紧急的. 更新用户输入过程中,事件的触发频率非常,会导致大量重新渲染,页面可能会出现卡顿 setKeyWord(e.target.value.trim()) }} /> <hr /> <Card searchQuery={keyWord}></Card> </div> ) }
Card.js
import React from 'react' import data from './data.json' // data.json中存储了很多github用户信息 export default function Card({ searchQuery }) { const filterData = data.filter((item) => { return item.login.includes(searchQuery) }) return ( <div className="row"> {filterData.map((item) => { return ( <div className="card" key={item.id}> <a target="_blank"> <img src={item.avatar_url} style={{ width: '100px' }} /> </a> <p className="card-text">{item.login}</p> </div> ) })} </div> ) }
data.json
[ { "login": "yi-ge", "id": 6657330, "node_id": "MDQ6VXNlcjY2NTczMzA=", "avatar_url": "https://avatars.githubusercontent.com/u/6657330?v=4", "gravatar_id": "", "url": "https://api.github.com/users/yi-ge", "html_url": "https://github.com/yi-ge", "followers_url": "https://api.github.com/users/yi-ge/followers", "following_url": "https://api.github.com/users/yi-ge/following{/other_user}", "gists_url": "https://api.github.com/users/yi-ge/gists{/gist_id}", "starred_url": "https://api.github.com/users/yi-ge/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/yi-ge/subscriptions", "organizations_url": "https://api.github.com/users/yi-ge/orgs", "repos_url": "https://api.github.com/users/yi-ge/repos", "events_url": "https://api.github.com/users/yi-ge/events{/privacy}", "received_events_url": "https://api.github.com/users/yi-ge/received_events", "type": "User", "site_admin": false, "score": 1.0 }, ...忽略其他信息 ]
我们可以使用useTransition
告诉react,更新文本框文字是紧急的,更新searchQuery是非紧急的.让react优先渲染文本框,然后再渲染用户列表
import React, { useState, useTransition } from 'react' import Card from './Card' export default function App() { const [keyWord, setKeyWord] = useState('') const [searchQuery, setSearchQuery] = useState('') // 调用 useTransition, 可以得到一个isPending和startTransition // isPending表示 非紧急渲染是否在等待中 是boolean值 // startTransition中定义state update(更新状态)的代码 const [isPending, startTransition] = useTransition() return ( <div> 请输入关键字: <input type="text" value={keyWord} onChange={(e) => { setKeyWord(e.target.value.trim()) //startTransition中定义state update代码 startTransition(() => { setSearchQuery(e.target.value.trim()) }) }} /> <hr /> {isPending && <Progress />} <Card searchQuery={searchQuery}></Card> </div> ) }
useDeferredValue
useDeferredValue
允许您推迟重新渲染树的非紧急部分。它类似于抖动,但与之相比有一些优点。没有固定的时间延迟,因此 React 将在第一次渲染反映在屏幕上后立即尝试延迟渲染。延迟渲染是可中断的,不会阻止用户输入
const [deferredValue] = useDeferredValue(value) // useDeferredValue 接受一个值并返回该值的新副本。如果当前渲染是紧急更新的结果,比如用户输入,应使用之前的值
import React, { useState, useDeferredValue } from 'react' import Card from './Card' import Progress from './Progress' export default function App() { const [keyWord, setKeyWord] = useState('') const deferredKey = useDeferredValue(keyWord) return ( <div> 请输入关键字: <input type="text" value={keyWord} onChange={(e) => { setKeyWord(e.target.value.trim()) }} /> <hr /> <Card searchQuery={deferredKey}></Card> </div> ) }