【收藏】面试官常问的 也是项目中常用的 ⚛ React Hooks

116 阅读4分钟

面试官常问的也是项目中常用的 ⚛ React Hooks:

  • useSate: 添加 状态
  • useEffect: 添加副作用
  • useContext: 访问 Context 上下文
  • useReducer:管理复杂的 state
  • useCallBack: 缓存函数
  • useMemo:缓存计算结果
  • useRef:引用dom元素 或 其他组件

项目中 常用 hooks

useSate: 添加 状态

react中非常常用的hook之一,就是管理组件的内部状态的,实现组件的数据相应更新。

function Example () {
    const [count, setCount] = useState(0)
    // ...
}

useEffect: 添加副作用

比如 发起网络请求 或者 操作dom 或者 订阅和取消订阅事件

1、发送 网络 请求 并 更新 组件状态

function example () {
    const [data, setData] = useState(null)
    
    useEffect(() => {
        
        const getList = async () => {
            const res = await axios.get('/api/list')
            setData(res.data)
        }
        
         getList()
       
    }, [])
    
    return (
        <>
            {
                data ? (
                    <ul>
                        {data.map(item => (
                            <li key={ item.id }> { item.title } </li>
                        ))}
                    </ul>
                ) : (
                    <div>- 空 -</div>
                )
            }
        </>
    )
}

请求接口,也就是发送网络请求并更新组件状态。

2、订阅和取消订阅事件

function Example () {
    const [count, setCount] = useState(0)
    
    useEffect(() => {
        const handleResize = () => {
            setCount(window.innerWidth)
        }
        
        window.addEventListener('resize', handleResize)
        
        return () => {
            window.removeEventListener('resize', handleResize)
        }
    })
    
    return <> 窗口宽: { count } </>
}

useEffect 被用于 订阅 和 取消订阅 window 对象的resize事件。

在组件挂载时, useEffect会执行一次,并订阅resize事件并更新组件状态。

当组件卸载时,useEffect会执行一次返回函数,并取消订阅resize事件。

**3、操作dom 元素 **

function Example () {
    const inputRef = useRef(null)
    
    useEffect(() => {
        inputRef.current.focus()
    }, [])
    
    return (
        <>
            <input type="text" ref={inputRef} />
        </>
    )
}

useEffect被用于操作dom元素。

当组件挂载时,useEffect会执行一次,并将input元素聚焦。 由于input元素是通过ref引用的,因此需要使用ref引用的,因此需要使用useRef来创建一个ref对象,并在useEffect中使用ref对象来引用input元素。

useContext: 访问 Context 上下文

useContext用于一种跨组件层级共享数据的方式的hook,通常用于在应用中传递数据,避免组件之间的繁琐传值。

案例 动态切换主题

假设我们有一个全局的主题,希望在应用中使用该主题并能够动态切换主题。

建一个context对象来存储主题信息:

// ThemeContext.jsx
import React from 'react'

const ThemeContext = React.createContext({
    theme: 'light',
    toggleTheme: () => {}
})

export default ThemeContext

根组件 提供 一个 themeProvider 组件来提供主题信息

用 useState来创建theme状态和toggleTheme的回调函数

然后 在themeProvider组件中传递value的prop

function App() {
    const [theme, setTheme] = useState('light')
    
    const toggleTheme = () => {
        setTheme(theme === 'light' ? 'dark' : 'light')
    }
    
    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            <div className={ theme === 'light' ? 'light' : 'dark' }>
                // 内容...
            </div>
        </ThemeContext.Provider>
    )
}

用一个 header去 获取以及调用

import ThemeContext from  './ThemeContext'

function Header () {
   const { theme, toggleTheme } = useContext(ThemeContext)
   
   return (
       <button onClick={ toggleTheme }>
           切换主题
       </button>
   )
}

useReducer:管理复杂的 state

通常我们使用useState来管理组件的内部状态,然而,在某些情况下,组件的状态可能会变得复杂或难以维护,这个时候我们就可以用useReducer来管理组件的状态。

function reducer(state, action) {
    // ...
}

function Counter () {
    const [state, dispatch] = useReudcer(reducer, { count : 0 })
}

当一个state 需要维护多个数据,并且它们之间相互依赖,业务代码只需要通过dispatch来更新state,繁杂的逻辑放在reducer函数中。

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, { count: 0 });

  return (
    <>
      { state.count }
      <button onClick={() => dispatch({ type: 'increment' })}>
        加
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        减
      </button>
    </>
  );
}

useCallBack: 缓存函数

避免函数不必要的重复运算

useCallback 用来返回一个函数,在父子组件传参或通过函数封装。

function Example () {
    const [count, setCount] = useState(0)

    const a = useCallback(() => {
        setCount(count + 1);

        return () => console.log(b)
    }, [b])

    return (
        <button onClick={a}>{ count }</button>
    )
}

返回的函数a会根据b的变化而变化,如果b始终未变化,a也不会重新生成,避免函数在不必要的情况下更新。

useMemo:缓存计算结果

避免函数不必要的重复运算

useMemo 用来做缓存的,只有当依赖项改变的时候才会发生变化,否则拿缓存的值,就不用在每次渲染的时候再做计算。

一般我们讲 缓存函数、讲缓存数组,这里举例说明一下 缓存组件

function ExpensiveComponent(props) {
    // 大量的计算逻辑
    const result = /\* 计算结果 \*/;

    return <div>{result}</div>;
}

function Example() {
    const memoizedComponent = useMemo(() => <ExpensiveComponent />, \[]);

    return ( <div> <p>Component: {memoizedComponent}</p> </div>
    );
}

useRef:引用dom元素 或 其他组件

实现自定义操作 (参照 上面 useEffect 的操作dom的例子)

面试官常问

useEffect的第二个参数,传空数组和传依赖数组有什么分别?

在react中,useEffect是一个常用的hook,它用于处理组件生命周期的副作用。

useEffect接受两个参数,第一个是要执行的函数,第二个是依赖数组(可选)。

当传递空数组[]时,useEffect只会在数组挂载和卸载时调用一次。这种情况下,useEffect不会监听任何变量,并且不会对组件进行重新渲染。

useEffect(() => {
    // 只在挂载和卸载时执行
})

当传递依赖数组时,useEffect会在组件挂载和依赖时更新时调用。当依赖项中的任何一个值发生变化时,useEffect都将被重新调用。

useEffect(() => {
    // 在挂载、依赖列表变化以及卸载时执行
}, [ dep1, dep2 ])

如果useEffect中使用了闭包函数,则应该确保所有引用的变量都在依赖项中被显示声明,否则可能会导致不必要的重新渲染或者无法获取最新的状态。(这个可以看一下经典的例子 useEffect中使用setTimeout 这个例子)。


最好的学习是输出