您现在的位置是:网站首页> 编程资料编程资料
React-hooks面试考察知识点汇总小结(推荐)_React_
2023-05-24
337人已围观
简介 React-hooks面试考察知识点汇总小结(推荐)_React_
什么是hooks?解决了什么问题?
Hooks 是react16.8新增特性,它可以使用一些state的新特性,简化逻辑复用,副作用统一数据。
Hooks就是把某个目标结果钩到某个可能会变化的数据源或者事件源上,那么当被钩到的数据或者事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果。
Hook 简介
Hook出世之前React存在的问题
在组件之间复用状态逻辑很难
React 没有提供将可复用性行为“附加”到组件的途径(例如,把组件连接到 store)。有一些解决此类问题的方案,比如 render props 和 高阶组件。但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。
复杂组件变得难以理解
组件常常在
componentDidMount和componentDidUpdate中获取数据。但是,同一个componentDidMount中可能也包含很多其它的逻辑,如设置事件监听,而之后需在componentWillUnmount中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。难以理解的 class
class 是学习 React 的一大屏障。你必须去理解 JavaScript 中
this的工作方式,这与其他语言存在巨大差异。还不能忘记绑定事件处理器。没有稳定的语法提案,这些代码非常冗余。大家可以很好地理解 props,state 和自顶向下的数据流,但对 class 却一筹莫展。
Hook带来的解决方案
- 你可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。
- Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。你还可以使用 reducer 来管理组件的内部状态,使其更加可预测。
- Hook 使你在非 class 的情况下可以使用更多的 React 特性。 从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。
Hook API
useState
useState 是react自带的一个hook函数,它的作用就是用来声明状态变量。useState这个函数接收的参数是我们的状态初始值(initial state),它返回了一个数组,这个数组的第[0]项是当前当前的状态值,第[1]项是可以改变状态值的方法函数。
初始化
//返回一个 state,以及更新 state 的函数 setState(接收一个新的 state 值并将组件的一次重新渲染加入队列) const [state, setState] = useState(initialState);
函数式更新
//如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。该函数将接收先前的 state,并返回一个更新后的值。 function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} > ); }惰性初始 state
//如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用 const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; }); 跳过 state 更新
调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 使用 Object.is 比较算法 来比较 state。)
useEffect
我们写的有状态组件,通常会产生很多的副作用(side effect),比如发起ajax请求获取数据,添加一些监听的注册和取消注册,手动修改dom等等。我们之前都把这些副作用的函数写在生命周期函数钩子里,比如componentDidMount,componentDidUpdate和componentWillUnmount。而现在的useEffect就相当与这些声明周期函数钩子的集合体。它以一抵三。
简单例子
import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // 类似于componentDidMount 和 componentDidUpdate: useEffect(() => { // 更新文档的标题 document.title = `You clicked ${count} times`; }); return ( You clicked {count} times
); }清除 effect
通常,组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个清除函数。以下就是一个创建订阅的例子:
useEffect(() => { const subscription = props.source.subscribe(); return () => { // 清除订阅 subscription.unsubscribe(); }; }); 为防止内存泄漏,清除函数会在组件卸载前执行。另外,如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除。
effect 的执行时机
与 componentDidMount、componentDidUpdate 不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用。这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因此不应在函数中执行阻塞浏览器更新屏幕的操作。
effect 的条件执行
默认情况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被重新创建。在某些情况下,我们不需要在每次组件更新时都创建新的订阅,而是仅需要在 source prop 改变时重新创建。要实现这一点,可以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组。
//此时,只有当 props.source 改变后才会重新创建订阅。(要实现componentDidMount功能只需要设置第二个参数为[]即可) useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], ); useContext
可以深层组件传值,父组件传给子孙组件。接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 的 value prop 决定。
当组件上层最近的 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染
const themes = { light: { foreground: "#000000", background: "#eeeeee" }, dark: { foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext(themes.light); function App() { return ( ); } function Toolbar(props) { return ( ); } function ThemedButton() { const theme = useContext(ThemeContext); return ( ); } useReducer
useState 的替代方案,可以用于复杂状态处理。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)参考 前端react面试题详细解答
指定初始 state
有两种不同初始化 useReducer state 的方式,你可以根据使用场景选择其中的一种。将初始 state 作为第二个参数传入 useReducer 是最简单的方法:
//nst [state, dispatch] = useReducer(reducer, initialArg, init); const [state, dispatch] = useReducer( reducer, {count: initialCount} ); 某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
const initialState = {count: 0}; 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, initialState); return ( <> Count: {state.count} > ); } 惰性初始化
你可以选择惰性地创建初始 state。为此,需要将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)。
这么做可以将用于计算 state 的逻辑提取到 reducer 外部,这也为将来对重置 state 的 action 做处理提供了便利:
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} > ); } 跳过 dispatch
如果 Reducer Hook 的返回值与当前 state 相同,React 将跳过子组件的渲染及副作用的执行。(React 使用 Object.is 比较算法 来比较 state。)
useMemo
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。memo是浅比较,意思是,对象只比较内存地址,只要你内存地址没变,管你对象里面的值千变万化都不会触发render。
你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。将来,React 可能会选择“遗忘”以前的一些 memoized 值,并在下次渲染时重新计算它们,比如为离屏组件释放内存。先编写在没有 useMemo 的情况下也可以执行的代码 —— 之后再在你的代码中添加 useMemo,以达到优化性能的目的。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback
把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
useMemo 与 useCallback 类似,都是有着缓存的作用,useMemo 是缓存值的,useCallback 是缓存函数的。
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], ); useRef
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
useEffect里面的state的值,是固定的,这个是有办法解决的,就是用useRef,可以理解成useRef的一个作用:就是相当于全局作用域,一处被修改,其他地方全更新。
本质上,useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。你应该熟悉 ref 这一种访问 DOM 的主要方式。如果你将 ref 对象以 形式传入组件,则无论该节点如何改
相关内容
- 基于 angular material theming 机制修改 mat-toolbar 的背景色(示例详解)_AngularJS_
- 微信小程序实战项目之富文本编辑器实现_javascript技巧_
- Swiper如何实现两行四列轮播图效果实例_javascript技巧_
- TypeScript开发小状况记录之选且只选一个_javascript技巧_
- vue+threejs写物体动画之物体缩放动画效果_vue.js_
- vue学习记录之动态组件浅析_vue.js_
- Vue生命周期实例分析总结_vue.js_
- Vue计算属性与侦听器和过滤器超详细介绍_vue.js_
- Vue路由参数的传递与获取方式详细介绍_vue.js_
- Vue守卫零基础介绍_vue.js_
