面试题答案
一键面试性能优化避免不必要重渲染
- 使用 React.memo 包裹高阶组件返回的组件:
- React.memo 是一个高阶组件,它会对 props 进行浅比较。如果 props 没有变化,组件不会重新渲染。例如:
import React from'react'; const withData = (WrappedComponent) => { return React.memo((props) => { // 这里处理高阶组件逻辑 return <WrappedComponent {...props} />; }); }; const MyComponent = ({ data }) => { return <div>{data}</div>; }; const EnhancedComponent = withData(MyComponent);
- 控制 prop 变化:
- 在高阶组件内部,确保传递给被包裹组件的 props 只有在必要时才发生变化。避免在高阶组件的 render 方法中创建新的对象或函数作为 props,因为这会导致被包裹组件认为 props 发生了变化从而重渲染。例如:
const withData = (WrappedComponent) => { const cachedData = {}; return (props) => { // 从缓存中获取数据,而不是每次都重新生成 const data = cachedData || {}; return <WrappedComponent {...props} data={data} />; }; };
- 使用 shouldComponentUpdate (类组件)或 useMemo (函数组件):
- 类组件:在高阶组件返回的类组件中,可以重写 shouldComponentUpdate 方法来手动控制组件更新。例如:
import React, { Component } from'react'; const withData = (WrappedComponent) => { return class extends Component { shouldComponentUpdate(nextProps) { // 自定义比较逻辑,只有当特定 prop 变化时才更新 return this.props.someProp!== nextProps.someProp; } render() { return <WrappedComponent {...this.props} />; } }; };
- 函数组件:在高阶组件返回的函数组件中,可以使用 useMemo 来缓存计算结果,只有当依赖项变化时才重新计算。例如:
import React, { useMemo } from'react'; const withData = (WrappedComponent) => { return (props) => { const data = useMemo(() => { // 复杂计算逻辑 return someComplexCalculation(props); }, [props.someDependency]); return <WrappedComponent {...props} data={data} />; }; };
优雅处理副作用
- 使用 useEffect (函数组件):
- 挂载和更新时执行副作用:在高阶组件返回的函数组件中,可以使用 useEffect 来处理副作用。例如,进行数据获取:
import React, { useEffect, useState } from'react'; const withData = (WrappedComponent) => { return (props) => { const [data, setData] = useState(null); useEffect(() => { const fetchData = async () => { const response = await fetch('/api/data'); const result = await response.json(); setData(result); }; fetchData(); }, []); return <WrappedComponent {...props} data={data} />; }; };
- 卸载时清理副作用:useEffect 可以返回一个清理函数,在组件卸载时执行。例如,取消订阅:
import React, { useEffect } from'react'; const withSubscription = (WrappedComponent) => { return (props) => { useEffect(() => { const subscription = someSubscriptionService.subscribe(() => { // 处理订阅更新 }); return () => { subscription.unsubscribe(); }; }, []); return <WrappedComponent {...props} />; }; };
- 使用 componentDidMount、componentDidUpdate 和 componentWillUnmount (类组件):
- 挂载时执行副作用:在高阶组件返回的类组件的 componentDidMount 方法中执行副作用,如数据获取:
import React, { Component } from'react'; const withData = (WrappedComponent) => { return class extends Component { state = { data: null }; async componentDidMount() { const response = await fetch('/api/data'); const result = await response.json(); this.setState({ data: result }); } render() { return <WrappedComponent {...this.props} data={this.state.data} />; } }; };
- 更新时执行副作用:在 componentDidUpdate 中可以根据 props 或 state 的变化执行副作用。例如:
import React, { Component } from'react'; const withData = (WrappedComponent) => { return class extends Component { state = { data: null }; async componentDidMount() { this.fetchData(); } async componentDidUpdate(prevProps) { if (prevProps.someProp!== this.props.someProp) { this.fetchData(); } } async fetchData() { const response = await fetch('/api/data'); const result = await response.json(); this.setState({ data: result }); } render() { return <WrappedComponent {...this.props} data={this.state.data} />; } }; };
- 卸载时清理副作用:在 componentWillUnmount 方法中清理副作用,如取消订阅:
import React, { Component } from'react'; const withSubscription = (WrappedComponent) => { return class extends Component { subscription; componentDidMount() { this.subscription = someSubscriptionService.subscribe(() => { // 处理订阅更新 }); } componentWillUnmount() { this.subscription.unsubscribe(); } render() { return <WrappedComponent {...this.props} />; } }; };
- 遵循函数式编程思想:
- 纯函数原则:高阶组件本身应该是一个纯函数,它接受一个组件并返回一个新的组件,且不应该有自身的副作用。在处理副作用时,如使用 useEffect 或生命周期方法,尽量保持逻辑的纯净和可预测。例如,数据获取函数应该是一个独立的纯函数,在副作用钩子中调用。
- 不可变数据:在高阶组件和被包裹组件之间传递数据时,使用不可变数据结构。例如,使用 immer 库来处理 state 的更新,确保数据的变化是可追踪和可预测的,符合函数式编程中数据不可变的原则。