面试官:相比于 class 类组件,hooks 到底解决了什

写在前面

相信使用 react 的小伙伴,都使用过 class 组件函数式组件

目前来说,函数式组件比较流行,基本上新开发的项目都是以函数式组件为主。

那相比于这两种方式,最直观的就是函数式组件引入了 hooks,在引入 hooks 之后函数式组件变得更加灵活,所以这个问题基本上就是考察你对两种组件的理解,今天就以我个人的理解给大家分享一下,希望可以帮助到大家!

1、状态逻辑复用

class 组件中状态逻辑难以复用,常常需要使用高阶组件或 Render Props。

hooks 引入了 useState、useReducer 等钩子,使得状态逻辑可以更容易地在不同组件之间共享和复用

下面我们来举个栗子看看:

假设我们有两个组件 Counter1Counter2,它们都需要使用 count 状态和 increment 函数

类组件:

在这个例子中,两个组件有相同的状态逻辑,但由于类组件的特性,我们无法很好地抽象出这些逻辑进行复用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
js复制代码class Counter1 extends React.Component {
state = { count: 0 };

increment = () => {
this.setState(prevState => ({ count: prevState.count + 1 }));
};

render() {
return (
<div>
<p>Counter 1: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}

class Counter2 extends React.Component {
state = { count: 0 };

increment = () => {
this.setState(prevState => ({ count: prevState.count + 1 }));
};

render() {
return (
<div>
<p>Counter 2: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}

使用hooks:

使用 hooks,我们可以将这些逻辑抽象为一个自定义 hook useCounter,这样在Counter1 和 Counter2中可以复用一个函数,而不需要重写多个相同的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
js复制代码import { useState } from 'react';

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

const increment = () => setCount(prevCount => prevCount + 1);

return { count, increment };
}

function Counter1() {
const { count, increment } = useCounter();

return (
<div>
<p>Counter 1: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}

function Counter2() {
const { count, increment } = useCounter();

return (
<div>
<p>Counter 2: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}

2、生命周期管理

class 组件中的生命周期方法(如 componentDidMount、componentDidUpdate 等)往往包含多个不相关的逻辑,导致代码不够清晰。

hooks 提供了 useEffect 钩子,根据依赖项的不同情况可以模拟这几个生命周期函数,也就是一个 hooks 可以实现多个生命周期。

1.模拟 componentDidMount

componentDidMount() :在组件被挂载到 DOM 后调用,通常用于发送网络请求、订阅事件等初始化操作。

使用useEffect: 依赖项为空数组,初次渲染执行

1
2
3
4
5
js复制代码  useEffect(() => {
console.log('Component mounted');
// 执行数据获取操作
fetchData();
}, []);

2.模拟 componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot): 在组件更新后调用,通常用于处理更新后的操作,比如更新 DOM 或进行网络请求。

使用useEffect: 依赖项为 state,state 变化时执行

1
2
3
4
5
js复制代码  useEffect(() => {
console.log('Component updated');
// 执行其他副作用操作
document.title = `You clicked ${count} times`;
}, [count]);

3.模拟 componentWillUnmount

componentWillUnmount() :在组件即将从 DOM 中移除时调用,用于清理工作,比如取消订阅、清除定时器等。

使用useEffect: 在组件卸载时执行 return,清理副作用

1
2
3
4
5
6
7
js复制代码  useEffect(() => {
return () => {
console.log('Component unmounted');
// 执行清理操作
clearSubscription();
};
}, []);

3、性能优化

hooks 可以帮助避免不必要的重新渲染和函数创建。,通过 useMemouseCallback 等钩子进行性能优化。

useMemo

用于记忆(缓存)计算结果,并在依赖项变化时重新计算。 这有助于避免不必要的重复计算,提高性能。

1
2
3
4
5
6
7
8
9
10
11
js复制代码    import React, { useMemo } from 'react';

function ExpensiveComponent({ prop1, prop2 }) {
// 使用 useMemo 缓存计算结果
const result = useMemo(() => {
// 进行昂贵的计算或处理
return prop1 + prop2;
}, [prop1, prop2]); // 依赖项数组

return <p>Result: {result}</p>;
}

useCallback

用于缓存回调函数,避免在每次渲染时重新创建。

接收一个回调函数和依赖项数组,返回缓存后的回调函数。

useCallback的依赖项参数用于指定哪些变量的变化会导致生成新的回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
js复制代码   import React, { useState, useCallback } from 'react';

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

const handleClick = useCallback(() => {
// 使用 count 进行逻辑处理
console.log(count);
}, [count]);

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ChildComponent onClick={handleClick} />
</div>
);
}

function ChildComponent({ onClick }) {
// 使用缓存后的回调函数
return <button onClick={onClick}>Click me</button>;
}

在这个例子中,useCallback缓存了 handleClick回调函数,并且指定了依赖项数组为[count]。这意味着只有当 count发生变化时,handleClick才会生成新的回调函数。

总结

hooks 的出现 让 React 函数组件也能够更好的进行状态复用以及使用 useEffect 模拟生命周期的实现,通过使用useMemouseCallback 避免不必要的重新渲染和函数创建,还提高了代码的可读性、复用性和可维护性

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%