只能在函数组件内部使用:React Hooks 只能在函数组件内部使用,不能在类组件中使用。这是因为 React Hooks 是基于函数式编程的思想,通过函数来管理组件的状态和生命周期。
只能在顶层调用:React Hooks 必须在函数组件的顶层调用,不能在条件语句、循环语句或嵌套函数中调用。这是为了保证 Hooks 的执行顺序和稳定性,以及避免对状态的错误管理。
Hook 名称必须以 “use” 开头:自定义的 Hook 名称必须以 “use” 开头,这是为了标识该函数是一个 Hook,并能够在其它 Hook 内使用。
必须按顺序调用:React Hooks 必须按照相同的顺序调用,且每次函数组件渲染时的调用顺序必须相同。这是为了确保在组件重新渲染时,React 能够正确地将之前的状态和效果与当前的状态和效果进行匹配。
不能在循环中使用 Hook:React Hooks 不能在循环中使用,因为循环中的闭包函数会共享相同的状态。如果需要在循环中使用 Hook,可以使用自定义 Hook 来解决这个问题。
需要注意的是,这些限制都是为了保证 React Hooks 的正确性和稳定性,遵守这些限制可以更好地使用和理解 React Hooks。
useEffect 和 useLayoutEffect 是 React Hooks 中用于处理副作用的两个钩子函数,它们之间的主要区别在于触发的时机不同。
useEffect 会在组件渲染完成后异步执行副作用代码。它不会阻塞组件的渲染,会在浏览器绘制完成后才执行。这意味着 useEffect 中的副作用代码可能会在页面重新渲染之后才执行,因此它适合处理不需要立即执行的副作用,如数据请求、事件绑定等。
useLayoutEffect 会在组件渲染完成后同步执行副作用代码。它会在浏览器绘制之前执行,即在页面重新渲染之前执行。这意味着 useLayoutEffect 中的副作用代码会在页面重新渲染之前执行,可能会阻塞组件的渲染,因此需要注意使用 useLayoutEffect 的性能影响。它适合处理需要在页面重新渲染之前立即执行的副作用,如获取 DOM 元素的尺寸、触发动画等。
总结来说,useEffect 是一种异步执行副作用代码的钩子函数,适合处理不需要立即执行的副作用;useLayoutEffect 是一种同步执行副作用代码的钩子函数,适合处理需要在页面重新渲染之前立即执行的副作用。根据具体的需求,选择合适的钩子函数来处理副作用,可以优化和控制组件的行为。
假设有下述场景:
移动端中,用户访问了一个列表页,上拉浏览列表页的过程中,随着滚动高度逐渐增加,数据也将采用触底分页加载的形式逐步增加,列表页浏览到某个位置,用户看到了感兴趣的项目,点击查看其详情,进入详情页,从详情页退回列表页时,需要停留在离开列表页时的浏览位置上
类似的数据或场景还有已填写但未提交的表单、管理系统中可切换和可关闭的功能标签等,这类数据随着用户交互逐渐变化或增长,这里理解为状态,在交互过程中,因为某些原因需要临时离开交互场景,则需要对状态进行保存
在 React 中,我们通常会使用路由去管理不同的页面,而在切换页面时,路由将会卸载掉未匹配的页面组件,所以上述列表页例子中,当用户从详情页退回列表页时,会回到列表页顶部,因为列表页组件被路由卸载后重建了,状态被丢失。
在 React 中,可以通过使用组件的状态和生命周期方法来实现状态自动保存(KeepAlive)的功能。下面是一种实现方式:
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} />;
}
};
};
import React from 'react';
const MyComponent = ({ count, setCount }) => {
// ...组件的实现
};
export default withKeepAlive(MyComponent);
这样,当这个被包装的组件被销毁时,其状态会被保存下来。当组件重新 Mount 时,之前保存的状态会被恢复,实现了状态的自动保存。
需要注意的是,这只是一种简单的实现方式,如果需要更复杂的 KeepAlive 功能,可以考虑使用 React 的 Context 或 Redux 等状态管理工具来实现。
MobX 和 Redux 都是用于状态管理的库,它们之间有以下区别:
哲学不同:MobX 倾向于提供简单和直观的 API,使得状态管理变得更加简单和自然。它使用可观察对象和自动追踪的方式来管理状态,通过对状态的直接修改来触发自动更新。而 Redux 则更倾向于函数式编程的思想,通过纯函数的方式对状态进行更新,通过触发 action 来派发更新。
数据流管理方式不同:在 MobX 中,状态的变化是自动的,当状态被观察的时候,任何对状态的修改都会被自动追踪并触发相关的更新。这种方式下,状态的更新是一种直接的、隐式的方式。而 Redux 中,状态的更新是显式的,通过派发 action 来改变状态,再通过 reducer 函数来返回新的状态。
API 复杂度不同:由于 MobX 的设计理念是提供简单和直观的 API,因此使用 MobX 可以更快地上手并进行状态管理。而 Redux 的设计更加严格,需要编写额外的 action 和 reducer 函数,因此在一些复杂的应用中,会需要更多的代码和配置。
性能差异:由于 MobX 使用了自动追踪的方式来管理状态,它可以做到更细粒度的更新,只更新相关的组件,因此在某些场景下,MobX 的性能可能会比 Redux 更好。但是,在一些需要更严格控制状态更新的场景下,Redux 可能更适合。
综上所述,MobX 更加面向简单和直观的状态管理,适用于快速开发和小型应用;而 Redux 更加严格和可控,适用于大型应用和需要严格控制状态更新的场景。根据具体的需求,选择合适的状态管理库可以提高开发效率和应用性能。
下面是一个简单的函数组件,有两个按钮:“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