主题
React Effect Event
首先有 3 个概念:
- Event 事件
- Effect 副作用
- Effect Event 副作用事件
Event 的概念
Event 的概念是由行为触发,而不是状态触发的逻辑。
Effect 的概念
Effect 是由某些状态触发,而不是某些行为触发的逻辑。 提交表单是一个 Event(由提及行为触发),Event 逻辑应该只写在事件回掉中,而不是 useEffect 中。
js
function App() {
const [data, updateData] = useState(null);
useEffect(() => {
fetchData().then((data) => {
// ...一些业务逻辑
// 更新data
updateData(data);
});
}, []);
function onSubmit(opt) {
fetchData(opt).then((data) => {
// ...一些业务逻辑
// 更新data
updateData(data);
});
}
// ...
}
但在实际项目中,随着逻辑越来越复杂,可能出现下面的代码:
js
useEffect(() => {
fetchData(options).then((data) => {
// ...一些业务逻辑
// 更新data
updateData(data);
});
}, [options, xxx, yyy, zzz]);
很难区分 fetchData 会在什么情况下执行,因为:
- useEffect 依赖项太多了
- 很难掌握每个依赖项变化的逻辑。
useEffect 的依赖问题
下面的代码 useEffect 里我要读取最新的 theme,但是不想 theme 添加到依赖列表里,因为不符合逻辑。
js
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
connection.on("connected", () => {
showNotification("连接成功!", theme);
});
return () => connection.disconnect();
}, [roomId, theme]);
这个时候 useEffectEvent 就登场了:
js
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification("连接成功!", theme);
});
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
connection.on("connected", () => {
onConnected();
});
return () => {
connection.disconnect();
};
}, [roomId]);
// ...
}
useEffectEvent 的实现的源码是:
js
function updateEvent(callback) {
const hook = updateWorkInProgressHook();
// 保存callback的引用
const ref = hook.memoizedState;
// 在useEffect执行前更新callback的引用
useEffectEventImpl({ ref, nextImpl: callback });
return function eventFn() {
if (isInvalidExecutionContextForEventFunction()) {
throw new Error("A function wrapped in useEffectEvent can't be called during rendering.");
}
return ref.impl.apply(undefined, arguments);
};
}
在 useEffect 执行前会将 callback 保存到 ref 的引用上,并且必须保证他在 Effect (isInvalidExecutionContextForEventFunction)里面执行,否则就会抛出一个错误。另外他每次都返回一个全新的函数 eventFn()
,而不是像 useCallback(()=>{}, [])
返回一个不变的引用。
总结
- Event 由某些行为触发
- Effect 由某些状态变化触发
- Effect Event 在 Effect 内执行,但 Effect 并不依赖其中的状态逻辑。并且它始终返回不同的引用。