【React】精选5题

1. React Hooks 在使用上有哪些限制?

  1. 只能在函数组件内部使用:React Hooks 只能在函数组件内部使用,不能在类组件中使用。这是因为 React Hooks 是基于函数式编程的思想,通过函数来管理组件的状态和生命周期。

  2. 只能在顶层调用:React Hooks 必须在函数组件的顶层调用,不能在条件语句、循环语句或嵌套函数中调用。这是为了保证 Hooks 的执行顺序和稳定性,以及避免对状态的错误管理。

  3. Hook 名称必须以 “use” 开头:自定义的 Hook 名称必须以 “use” 开头,这是为了标识该函数是一个 Hook,并能够在其它 Hook 内使用。

  4. 必须按顺序调用:React Hooks 必须按照相同的顺序调用,且每次函数组件渲染时的调用顺序必须相同。这是为了确保在组件重新渲染时,React 能够正确地将之前的状态和效果与当前的状态和效果进行匹配。

  5. 不能在循环中使用 Hook:React Hooks 不能在循环中使用,因为循环中的闭包函数会共享相同的状态。如果需要在循环中使用 Hook,可以使用自定义 Hook 来解决这个问题。

需要注意的是,这些限制都是为了保证 React Hooks 的正确性和稳定性,遵守这些限制可以更好地使用和理解 React Hooks。

2. useEffect 与 useLayoutEffect 有什么区别?

useEffect 和 useLayoutEffect 是 React Hooks 中用于处理副作用的两个钩子函数,它们之间的主要区别在于触发的时机不同。

useEffect 会在组件渲染完成后异步执行副作用代码。它不会阻塞组件的渲染,会在浏览器绘制完成后才执行。这意味着 useEffect 中的副作用代码可能会在页面重新渲染之后才执行,因此它适合处理不需要立即执行的副作用,如数据请求、事件绑定等。

useLayoutEffect 会在组件渲染完成后同步执行副作用代码。它会在浏览器绘制之前执行,即在页面重新渲染之前执行。这意味着 useLayoutEffect 中的副作用代码会在页面重新渲染之前执行,可能会阻塞组件的渲染,因此需要注意使用 useLayoutEffect 的性能影响。它适合处理需要在页面重新渲染之前立即执行的副作用,如获取 DOM 元素的尺寸、触发动画等。

总结来说,useEffect 是一种异步执行副作用代码的钩子函数,适合处理不需要立即执行的副作用;useLayoutEffect 是一种同步执行副作用代码的钩子函数,适合处理需要在页面重新渲染之前立即执行的副作用。根据具体的需求,选择合适的钩子函数来处理副作用,可以优化和控制组件的行为。

3. React 中怎么实现状态自动保存(KeepAlive)?

什么是状态保存?

假设有下述场景:

移动端中,用户访问了一个列表页,上拉浏览列表页的过程中,随着滚动高度逐渐增加,数据也将采用触底分页加载的形式逐步增加,列表页浏览到某个位置,用户看到了感兴趣的项目,点击查看其详情,进入详情页,从详情页退回列表页时,需要停留在离开列表页时的浏览位置上

类似的数据或场景还有已填写但未提交的表单、管理系统中可切换和可关闭的功能标签等,这类数据随着用户交互逐渐变化或增长,这里理解为状态,在交互过程中,因为某些原因需要临时离开交互场景,则需要对状态进行保存

在 React 中,我们通常会使用路由去管理不同的页面,而在切换页面时,路由将会卸载掉未匹配的页面组件,所以上述列表页例子中,当用户从详情页退回列表页时,会回到列表页顶部,因为列表页组件被路由卸载后重建了,状态被丢失。

在 React 中,可以通过使用组件的状态和生命周期方法来实现状态自动保存(KeepAlive)的功能。下面是一种实现方式:

  1. 创建一个高阶组件(Higher-Order Component,HOC)来包装需要进行状态自动保存的组件。这个高阶组件的作用是在组件销毁时将组件的状态保存下来,并在组件重新Mount时将之前保存的状态恢复。
import React, { Component } from 'react';

const withKeepAlive = WrappedComponent => {
  return class extends Component {
    constructor(props) {
      super(props);
      this.state = {
        savedState: null
      };
    }

    componentDidMount() {
      if (this.state.savedState) {
        this.setState({ savedState: null });
      }
    }

    componentWillUnmount() {
      this.setState({ savedState: this.props });
    }

    render() {
      const { savedState } = this.state;
      if (savedState) {
        return <WrappedComponent {...savedState} />;
      }
      return <WrappedComponent {...this.props} />;
    }
  };
};
  1. 在需要进行状态自动保存的组件上使用这个高阶组件进行包装。
import React from 'react';

const MyComponent = ({ count, setCount }) => {
  // ...组件的实现
};

export default withKeepAlive(MyComponent);

这样,当这个被包装的组件被销毁时,其状态会被保存下来。当组件重新 Mount 时,之前保存的状态会被恢复,实现了状态的自动保存。

需要注意的是,这只是一种简单的实现方式,如果需要更复杂的 KeepAlive 功能,可以考虑使用 React 的 Context 或 Redux 等状态管理工具来实现。

4. mobx 和 redux 有什么区别?

MobX 和 Redux 都是用于状态管理的库,它们之间有以下区别:

  1. 哲学不同:MobX 倾向于提供简单和直观的 API,使得状态管理变得更加简单和自然。它使用可观察对象和自动追踪的方式来管理状态,通过对状态的直接修改来触发自动更新。而 Redux 则更倾向于函数式编程的思想,通过纯函数的方式对状态进行更新,通过触发 action 来派发更新。

  2. 数据流管理方式不同:在 MobX 中,状态的变化是自动的,当状态被观察的时候,任何对状态的修改都会被自动追踪并触发相关的更新。这种方式下,状态的更新是一种直接的、隐式的方式。而 Redux 中,状态的更新是显式的,通过派发 action 来改变状态,再通过 reducer 函数来返回新的状态。

  3. API 复杂度不同:由于 MobX 的设计理念是提供简单和直观的 API,因此使用 MobX 可以更快地上手并进行状态管理。而 Redux 的设计更加严格,需要编写额外的 action 和 reducer 函数,因此在一些复杂的应用中,会需要更多的代码和配置。

  4. 性能差异:由于 MobX 使用了自动追踪的方式来管理状态,它可以做到更细粒度的更新,只更新相关的组件,因此在某些场景下,MobX 的性能可能会比 Redux 更好。但是,在一些需要更严格控制状态更新的场景下,Redux 可能更适合。

综上所述,MobX 更加面向简单和直观的状态管理,适用于快速开发和小型应用;而 Redux 更加严格和可控,适用于大型应用和需要严格控制状态更新的场景。根据具体的需求,选择合适的状态管理库可以提高开发效率和应用性能。

5. 下面函数组件的输出分别是什么?

下面是一个简单的函数组件,有两个按钮:“alert”、“add”。

如果先点击“alert”按钮,再点击一次“add”按钮,那么弹窗框中的值和页面中展示value分别是什么?

const FunctionComponent = () => {
  const [value, setValue] = useState(1)

  const log = () => {
    setTimeout(() => {
      alert(value)
    }, 3000);
  }

  return (
    <div>
      <p>FunctionComponent</p>
      <div>value: {value}</div>
      <button onClick={log}>alert</button>
      <button onClick={() => setValue(value + 1)}>add</button>
    </div>
  )
}

答:
展示的value是2,alert的值是1

我们发现弹出的值和当前页面显示的值不相同。

换句话说:log 方法内的 value 和点击动作触发那一刻的 value 相同,value 的后续变化不会对 log 方法内的 value 造成影响。

这种现象被称为“闭包陷阱”或者被叫做“Capture Value” :函数式组件每次render 都会生产一个新的 log 函数,这个新的 log 函数会产生一个在当前这个阶段 value 值的闭包。

上面例子 “闭包陷阱” 的分析:

  1. 初始次渲染,生成一个 log 函数(value = 1)
  2. value 为 1 时,点击 alert 按钮执行 log 函数(value = 1)
  3. 点击按钮增加 value,比如 value 增加到 6,组件 render ,生成一个新的 log 函数(value = 6)
  4. 计时器触发,log 函数(value = 1)弹出闭包内的 value 为 1
    如何让弹窗中展示最新的value值呢?
  • 使用函数式更新:在使用状态更新函数时,可以传递一个回调函数,而不是直接使用状态的值。这样可以确保回调函数中引用的是最新的状态值。

  • 使用 useEffect 进行清理:如果在 useEffect 中使用了闭包引用了状态或属性,需要在 useEffect 的返回函数中进行清理。这样可以确保在组件卸载时,闭包中对状态或属性的引用被释放。

  • 使用 useRef 或 useCallback:可以使用 useRef 或 useCallback 来缓存函数或值,以避免闭包陷阱。这样可以确保引用的是固定的函数或值,不受组件重新渲染的影响。

举个例子:
使用 useRef 解决闭包陷阱的问题

const FunctionComponent = () => {
  const [value, setValue] = useState(1)
  const countRef = useRef(value)

  const log = () => {
    setTimeout(() => {
      alert(countRef.current)
    }, 3000);
  }

  useEffect(() => {
    countRef.current = value
  }, [value])

  return (
    <div>
      <p>FunctionComponent</p>
      <div>value: {value}</div>
      <button onClick={log}>alert</button>
      <button onClick={() => setValue(value + 1)}>add</button>
    </div>
  )
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

godlike-icy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值