- Published on
Effect 相关
- Authors

- Name
- 李丹秋
Effect 允许你指定由渲染本身,而不是特定事件引起的副作用, Effect的触发时机是在视图更新之后。
你可能不需要Effect
什么时候需要Effect
effect通常用于与一些外部的系统进行同步,比如浏览器API、三方DOM、网络请求等。如果只想根据状态而调整某些状态,那么是不需要Effect的
Effect特性
默认情况下React每次重新渲染之后,都会触发Effect.所以应该避免在Effect中执行State相关操作。
- 如果没有传递依赖项数组,则每次重新渲染都会触发Effect
- 如果依赖项是空数组,则Effect只执行一次
- 如果依赖项数组中有明确的State变量,则只有相关State发生改变的时候,才会执行Effect 你无法选择依赖项,依赖项完全取决于Effect中的代码
为什么依赖数组中,可以省略Ref?
function VideoPlayer({ src, isPlaying }) {
const ref = useRef(null);
useEffect(() => {
if (isPlaying) {
ref.current.play();
} else {
ref.current.pause();
}
}, [isPlaying]);
因为Ref对象是一个稳定的标识符,React保证每次渲染Ref对象的引用都是相同的,永远不会改变,所有依赖数组中写不写都无所谓。 如果一些函数只需要执行一次,完全可以放在渲染函数中执行,而不是在Effect中
如何计算计算耗时?
console.time('filter array');
const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd('filter array');
console.time('filter array');
const visibleTodos = useMemo(() => {
return getFilteredTodos(todos, filter); // Skipped if todos and filter haven't changed
}, [todos, filter]);
console.timeEnd('filter array');
什么时候不需要Effect?
遵循一个大的原则,凡是事件触发的形式,都可以不需要Effect,Effect只去处理渲染所依赖的情况了
响应式Effect的生命周期
React如何重新同步Effect
const [show, setShow] = useState(false);
return (
<>
<label>
选择聊天室:{' '}
<select
value={roomId}
onChange={e => setRoomId(e.target.value)}
>
<option value="general">所有</option>
<option value="travel">旅游</option>
<option value="music">音乐</option>
</select>
</label>
<button onClick={() => setShow(!show)}>
{show ? '关闭聊天' : '打开聊天'}
</button>
{show && <hr />}
{show && <ChatRoom roomId={roomId} />}
</>
);
}
- 当依赖项目发生改变的时候,会先去执行卸载的操作,然后重新执行链接
- 当组件卸载的时候,断开链接 在严格模式下,Effect会执行两次,目的就是暴露潜在的风险,比如可能没有卸载相关链接
React会验证Effect代码中使用的每个响应式值是否已声明为其依赖项。如果一些值不会因为重新渲染而改变,则可以将他们移动到组件外部或者Effect内部。 写代码的时候要着重关注Effect依赖检查
将事件从Effect中分离
import { useState, useEffect } from 'react';
import { createConnection, sendMessage } from './chat.js';
import { showNotification } from './notifications.js';
const serverUrl = 'https://localhost:1234';
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
return () => connection.disconnect();
}, [roomId, theme]);
return <h1>Welcome to the {roomId} room!</h1>
}
export default function App() {
const [roomId, setRoomId] = useState('general');
const [isDark, setIsDark] = useState(false);
return (
<>
<label>
Choose the chat room:{' '}
<select
value={roomId}
onChange={e => setRoomId(e.target.value)}
>
<option value="general">general</option>
<option value="travel">travel</option>
<option value="music">music</option>
</select>
</label>
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Use dark theme
</label>
<hr />
<ChatRoom
roomId={roomId}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
这种情况当theme改变的时候,聊天也会重练,这肯定是不符合我们预期的。所以需要将showNotification这个非响应式的逻辑和周围Effect隔离开来。
import { useState, useEffect } from 'react';
import { experimental_useEffectEvent as useEffectEvent } from 'react';
import { createConnection, sendMessage } from './chat.js';
import { showNotification } from './notifications.js';
const serverUrl = 'https://localhost:1234';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]);
return <h1>Welcome to the {roomId} room!</h1>
}
export default function App() {
const [roomId, setRoomId] = useState('general');
const [isDark, setIsDark] = useState(false);
return (
<>
<label>
Choose the chat room:{' '}
<select
value={roomId}
onChange={e => setRoomId(e.target.value)}
>
<option value="general">general</option>
<option value="travel">travel</option>
<option value="music">music</option>
</select>
</label>
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Use dark theme
</label>
<hr />
<ChatRoom
roomId={roomId}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
可以将useEffectEvent类比成为一个事件,它发生在某个准确的时刻,而不是依赖于Effect响应