728x90
리액트 공식문서 학습기록
https://react.dev/learn
Separating Events from Effects
이벤트 핸들러와 Effect 중에 선택
이벤트 핸들러 - 상호작용될때(수동)
Effect - 동기화가 필요할때(자동)
반응 값 및 반응 논리
컴포넌트 내부에 선언된 props, state 및 변수를 반응값이라고 함.
- 이벤트 핸들러 내부의 로직은 반응하지 않음.
- 이펙트 내부의 로직은 반응적임
Effect에서 비반응 로직 추출
useEffectEvent를 사용하자.
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]); // ✅ All dependencies declared
// ...
useEffectEvent의 한계
- Effect 앞에 선언하고 내부에서만 호출할 것
- 다른 컴포넌트나 hook에 전달하지 말 것
과제 1
+, - 버튼 미동작
👉 increment 종속성 추가
import { useState, useEffect } from 'react';
const Timer = () => {
const [count, setCount] = useState(0);
const [increment, setIncrement] = useState(1);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + increment);
}, 1000);
return () => {
clearInterval(id);
};
}, [increment]);
return (
<>
<h1>
Counter: {count}
<button onClick={() => setCount(0)}>Reset</button>
</h1>
<hr />
<p>
Every second, increment by:
<button disabled={increment === 0} onClick={() => {
setIncrement(i => i - 1);
}}>–</button>
<b>{increment}</b>
<button onClick={() => {
setIncrement(i => i + 1);
}}>+</button>
</p>
</>
);
}
과제 2
+, - 버튼을 빠르게 누르면 타이머가 정지된 것처럼 보임
👉 useEffectEvent로 1초마다 실행되도록 변경
import { useState, useEffect } from 'react';
import { experimental_useEffectEvent as useEffectEvent } from 'react';
const Timer = () => {
const [count, setCount] = useState(0);
const [increment, setIncrement] = useState(1);
const onTick = useEffectEvent(() => {
setCount(c => c + increment);
});
useEffect(() => {
const id = setInterval(() => {
onTick();
}, 1000);
return () => {
clearInterval(id);
};
}, []);
return (
<>
<h1>
Counter: {count}
<button onClick={() => setCount(0)}>Reset</button>
</h1>
<hr />
<p>
Every second, increment by:
<button disabled={increment === 0} onClick={() => {
setIncrement(i => i - 1);
}}>–</button>
<b>{increment}</b>
<button onClick={() => {
setIncrement(i => i + 1);
}}>+</button>
</p>
</>
);
}
export default Timer;
과제 3
딜레이 조정 버튼이 1초보다 작으면 동작되지 않는 버그 수정
👉 onMount 함수의 내용을 useEffectEvent로 분리하지말고 useEffect 내부로 가져와서 delay 종속성에 반응하게 함.
import { useState, useEffect } from 'react';
import { experimental_useEffectEvent as useEffectEvent } from 'react';
const Timer = () => {
const [count, setCount] = useState(0);
const [increment, setIncrement] = useState(1);
const [delay, setDelay] = useState(100);
const onTick = useEffectEvent(() => {
setCount(c => c + increment);
});
useEffect(() => {
const id = setInterval(() => {
onTick();
}, delay);
return () => {
clearInterval(id);
}
}, [delay]);
return (
<>
<h1>
Counter: {count}
<button onClick={() => setCount(0)}>Reset</button>
</h1>
<hr />
<p>
Increment by:
<button disabled={increment === 0} onClick={() => {
setIncrement(i => i - 1);
}}>–</button>
<b>{increment}</b>
<button onClick={() => {
setIncrement(i => i + 1);
}}>+</button>
</p>
<p>
Increment delay:
<button disabled={delay === 100} onClick={() => {
setDelay(d => d - 100);
}}>–100 ms</button>
<b>{delay} ms</b>
<button onClick={() => {
setDelay(d => d + 100);
}}>+100 ms</button>
</p>
</>
);
}
과제 4
빠르게 방을 변경했을때 마지막 알림만을 출력하도록 변경
👉 connectedRoomId를 useEffectEvent의 매개변수로 변경하여 전달, 타임아웃에 id를 설정하고 정리해줌.
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';
const ChatRoom = ({ roomId, theme }) => {
const onConnected = useEffectEvent(connectedRoomId => {
showNotification('Welcome to ' + connectedRoomId, theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
let notificationTimeoutId;
connection.on('connected', () => {
notificationTimeoutId = setTimeout(() => {
onConnected(roomId);
}, 2000);
});
connection.connect();
return () => {
connection.disconnect();
if (notificationTimeoutId !== undefined) {
clearTimeout(notificationTimeoutId);
}
};
}, [roomId]);
return <h1>Welcome to the {roomId} room!</h1>
}
const 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'}
/>
</>
);
}
export default App;
'공부공부 > React' 카테고리의 다른 글
[react 공식문서] 30 Reusing Logic with Custom Hooks (0) | 2024.02.14 |
---|---|
[react 공식문서] 29 Removing Effect Dependencies (1) | 2024.02.14 |
[react 공식문서] 27 Lifecycle of Reactive Effects (0) | 2024.02.14 |
[react 공식문서] 26 You Might Not Need an Effect (0) | 2024.02.14 |
[react 공식문서] 25 Synchronizing with Effects (0) | 2024.02.14 |