redux(6)에서 만들었던 toolkit app을 분석하면서 기본적인 toolkit API를 알아보자.
App.tsx
function App() {
const [isTestOpen, setISTestOpen] = useState(true);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<button onClick={() => setISTestOpen(prev => !prev)}>Toggle</button> // 추가
{isTestOpen && <Test />} // 추가
<Counter />
</header>
</div>
);
}
features/counter/Test.tsx
import React, { useEffect } from 'react'
import { useAppDispatch } from '../../app/hooks'
import { incrementAsync } from './counterSlice';
const Test = () => {
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(incrementAsync(10));
return () => {
}
}, [])
return (
<div>test</div>
)
}
export default Test
먼저 App에서 토글 버튼을 만들고 state 변수가 true라면은 Test 컴포넌트를 마운트한다. 그러면 useEffect()로 인해 incrementAsync()가 호출된다.
이 상태로 앱을 실행해보면
맨 처음에 앱을 실행하면 state변수가 true이므로 Test 컴포넌트는 마운트되고 비동기 요청을 보내기 때문에 pending, fulfilled가 보임을 알 수 있다.
counter 앱에도 10이 증가함을 알 수 있다.
하지만 비동기 요청 도중에 Test 컴포넌트가 unmount가 된다면 어떨까? toggle 버튼을 두 번 빠르게 눌러 생성하자마자 없애보면
그럼에도 반영이 잘 되고 있다.
우리는 useEffect()의 clean up 함수를 통해서 이를 해결할 수 있다.
답은 간단하다. dispatch()함수는 promise 객체를 반환하고 우리는 그 객체를 받은 다음, clean up 함수에 promise.abort()를 추가해주면 된다.
Test.tsx
import React, { useEffect } from 'react'
import { useAppDispatch } from '../../app/hooks'
import { incrementAsync } from './counterSlice';
const Test = () => {
const dispatch = useAppDispatch();
useEffect(() => {
const promise = dispatch(incrementAsync(10));
return () => {
promise.abort()
}
}, [])
return (
<div>test</div>
)
}
export default Test
그러면 위와 같이 빠르게 컴포넌트를 마운트 후 바로 언마운트 시키면 비동기 요청이 reject됨을 알 수 있다.
counterSlice.tsx
export const fetchUsesAsync = createAsyncThunk(
'counter/fetchUsers',
async() => {
await axios.get("https://jsonplaceholder.typicode.com/users")
}
)
Test.tsx
const Test = () => {
const dispatch = useAppDispatch();
useEffect(() => {
// const promise = dispatch(incrementAsync(10));
const promise = dispatch(fetchUsesAsync());
return () => {
promise.abort()
}
}, [])
return (
<div>test</div>
)
}
이번에는 비동기 요청 중에 request를 보내는 요청으로 이전의 상황과 똑같은 상황을 재현했다. 하지만 rejected가 되었음에도 불구하고 요청에 대한 답이 돌아온다. 이를 막기 위해서는 request 자체를 취소하면 된다.
이를 구현하기 위해서는 async thunk 함수에서 두번째 인자로 넘어오는 thunkAPI객체를 받은 후에,
이 세 가지를 추가로 구현하면
export const fetchUsesAsync = createAsyncThunk(
'counter/fetchUsers',
async(_, thunkAPI) => {
const controller = new AbortController();
thunkAPI.signal.addEventListener('abort', () => {
controller.abort();
});
await axios.get("https://jsonplaceholder.typicode.com/users", {
signal: controller.signal
})
}
)
이와 같은 thunk 함수가 생성된다.
이 외에도 어떻게 하면은 slice를 생성하고 등록하는지, 또 thunk함수는 reducer로 어떻게 등록을 하는 지에 대해서는 counter 앱의 counterSlices.tsx를 분석해서 보도록 하자