面试官常问的也是项目中常用的 ⚛ 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 这个例子)。
最好的学习是输出