React hook && 函数组件相关

    技术2024-11-12  8

    React hook && 函数组件相关

    Hook 是 React 16.8 的新增特性,函数组件每次调用其生产的hook类型、顺序、数量应该都是一致的。不然会报错uncaught Invariant Violation: Rendered more/less hooks than during the previous render.

    1.useState

    import { useState } from 'react'; const [columns, setColumns] = useState([]);

    2.useEffect

    如果熟悉 React 类声明周期方法,可以把 useEffect Hook 视作 componentDidMount、componentDidUpdate 和 componentWillUnmount 的组合体

    与 componentDidMount 和 componentDidUpdate 不同,使用 useEffect 调度的副作用不会阻塞浏览器更新屏幕。这使得 application 感觉上具有响应式。大多数副作用不需要同步发生。而如果需要同步进行,(比如测量布局),有一个单独的 useLayoutEffect Hook, API 和 useEffect 相同。

    从 effect 中返回一个 function? 这是 effect 可选的清理机制。每个 effect 都可以返回一个在它之后清理的 function。这使得我们能够保持添加订阅和删除订阅彼此接近的订阅的逻辑。这同样是 effect 的一部分。

    在某些情况下,每次 render 后清理或者使用 effect 可能会产生性能问题。在类组件中,可以通过 componentDidUpdate 中编写 prevProps 或 prevState 的额外比较来解决这个问题:

    componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { document.title = `You clicked ${this.state.count} times`; } }

    这个要求很常见,而这种方式已经被内置到 useEffect Hook 的 API中,如果在重新渲染之间没有更新某些值,则可以告诉 React 跳过 effect,为了实现这种方式,需要将数组作为可选的第二个参数传递给 useEffect:

    import { useEffect } from 'react'; useEffect(() => { document.title = `You clicked ${count} times`; }, [count]);// 只有在 count 发生变化的时候才会执行这个 effect

    3.useRequest

    const { data: latestPipelineInstanceAndQualityReportData } = useRequest( async () => { const instance = await pipelineInstanceApi.getLatestByBranch(branchType, Number(branchId)); let imageQuality = null; if (instance) { imageQuality = await pipelineInstanceApi.getImageQuality(instance.id); } return { instance, imageQuality }; }, { refreshDeps: [branchId], }, );

    以及

    const { data: monitorPageConfList, loading } = useRequest( async () => { return monitorApi.getMonitorPageConf(); }, { refreshDeps: [], onError: (error) => { message.error(`获取监控配置失败:${error.message}`); } }, ); if (loading) {

    4.useContext

    新建一个中间文件context.tx,内容为:

    export const codeReviewContext = React.createContext({});

    父组件这么写:

    import { codeReviewContext } from './context'; 尖括号codeReviewContext.Provider value={{ branchId, branchType, demandId, tab, gitUrl, uri, query, isReviewer, thumbsUped }} > 尖括号子组件/> 尖括号/codeReviewContext.Provider>

    5.useCallback

    使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。

    import React, { useCallback } from 'react'; const getReleaseLog = useCallback(async (service_name) => { if (!service_name) { return Promise.resolve([]); } try { const diff_list = await serviceApi.diffRequirement(service_name); return Promise.resolve(diff_list || []); } catch (err) { message.error('getChangeLog Error: ' + err.message, 3); console.error('---fetch ChangelogContent error: ', err.message); return Promise.resolve([]); } }, []);

    6.useMemo

    import { useMemo } from 'react'; function Button({ name, children }) { function changeName(name) { console.log('11') return name + '改变name的方法' } const otherName = useMemo(()=>changeName(name),[name]) return ( <> <div>{otherName}</div> <div>{children}</div> </> ) } export default Button

    这个时候我们点击 改变content值的按钮,发现changeName 并没有被调用。 但是点击改变name值按钮的时候,changeName被调用了。 所以我们可以使用useMemo方法 避免无用方法的调用,当然一般我们changName里面可能会使用useState来改变state的值,那是不是就避免了组件的二次渲染。 达到了优化性能的目的

    7.useRef

    一个常见的用例是强制访问子组件:

    import { useRef } from 'react'; function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { // `current` points to the mounted text input element inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }

    另一个例子是异步回调函数里面要获取某个变量的最新值而不是当时的快照值

    import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + count); }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }

    改写为:

    function Example() { const [count, setCount] = useState(0); const latestCount = useRef(count); useEffect(() => { // Set the mutable latest value latestCount.current = count; setTimeout(() => { // Read the mutable latest value console.log(`You clicked ${latestCount.current} times`); }, 3000); }); // ... } import React, { useState, useRef } from 'react'; function Counter() { const [count, setCount] = useState(0); const latestCount = useRef(count); useEffect(() => { latestCount.current = count; }); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + latestCount.current); }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }

    7.useLayoutEffect

    用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制.

    8.react.memo

    引入于16.6.0 react.memo的实现很简单,就几行代码:

    export default function memo<Props>( type: React$ElementType, compare?: (oldProps: Props, newProps: Props) => boolean, ) { if (__DEV__) { if (!isValidElementType(type)) { warningWithoutStack( false, 'memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type, ); } } return { $$typeof: REACT_MEMO_TYPE, type, compare: compare === undefined ? null : compare, }; }
    Processed: 0.043, SQL: 12