// src/redux/modules/counterSlice.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
//createAsyncThunk 추가 임포트
//...중략
//thunk------------------------------->↓ 추가
export const __addNumber = createAsyncThunk(
"counter/__addNumber", // 첫번째 인자 : action value
// 두번째 인자 : 함수
(args, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(addNumber(args));
}, 3000);
// dispatch하기 전에 setTimeout을 통해 5초 후에 dispatch되면서 counterSlice가 실행되면서 globalState가 업데이트 된다.
}
);
thunk함수를 사용하여 counter가 5초 후에 + - 되도록 구현해보았다.
코드 상태는 아래와 같다.
//src/redux/modules/counter.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
const initialState = {
number: 0,
};
export const __addNumber = createAsyncThunk("counter/__addNumber", (args, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(addNumber(args));
}, 3000);
});
export const __minusNumber = createAsyncThunk("counter/__minusNumber", (args, thunkAPI) => {
setTimeout(() => {
thunkAPI.dispatch(minusNumber(args));
}, 3000);
});
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
addNumber: (state, action) => {
state.number = state.number + action.payload;
},
minusNumber: (state, action) => {
state.number = state.number + action.payload;
},
},
});
export const { addNumber, minusNumber } = counterSlice.actions;
export default counterSlice.reducer;
//src/App.js
import React from "react";
import Router from "./shared/Router";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __addNumber, __minusNumber } from "./redux/modules/counter";
const App = () => {
const dispatch = useDispatch();
const [number, setNumber] = useState(0);
const globalNumber = useSelector((state) => state.counter.number);
const onChangeHandler = (e) => {
const { value } = e.target;
setNumber(+value);
};
const onClickAddNumberHandler = () => {
dispatch(__addNumber(number));
};
const onClickMinusNumberHandler = () => {
dispatch(__minusNumber(number));
};
return (
<div>
<div>{globalNumber}</div>
<input type="number" onChange={onChangeHandler} />
<button onClick={onClickAddNumberHandler}>더하기</button>
<button onClick={onClickMinusNumberHandler}>빼기</button>
<Router />
</div>
);
};
export default App;
//src/config/configStore.js
import { configureStore } from "@reduxjs/toolkit";
import counter from "../modules/counter";
const store = configureStore({
reducer: {
counter,
},
});
export default store;
data
, isLoading
, error
로 관리한다.data
data
는 todos
이다.isLoading
이란?false
isLoading
의 변화false
: 초기 상태true
: 서버 통신 중일 때 true가 된다. (값을 가져오는 상태)false
: 통신이 끝날 때 false로 바뀐다.error
란?//src/modules/todos.js
const initialState = {
todos: [], // data
isLoading: false, // false : 초기 상태
error: null, // 초기에는 error가 없어서 null로 지정한것
};
//db.json
{
"todos": [{ "id": 1, "title": "hello world!" }]
}
//src/modules/todos.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = {
todos: [],
isLoading: false, // 서버에서 데이터를 가져올 때의 상태를 나타낼 값
error: null, // 서버에서 데이터 가져오다가 실패했을 때의 에러메세지 값
};
export const __getTodos = createAsyncThunk("todos/getTodos", async (payload, thunkAPI) => {
try {
//try : 성공 시
const data = await axios.get("http://localhost:3001/todos");
console.log(data.data);
return thunkAPI.fulfillWithValue(data.data);
} catch (error) {
// catch : 실패 시
console.log(error);
return thunkAPI.rejectWithValue(error);
}
});
export const todosSlice = createSlice({
name: "todos",
initialState,
reducers: {},
extraReducers: {
[__getTodos.pending]: (state) => {
console.log("pending 상태", state);
state.isLoading = true;
},
[__getTodos.fulfilled]: (state, action) => {
console.log("fufilled 상태", state, action);
state.isLoading = false;
state.todos = action.payload;
},
[__getTodos.rejected]: (state, action) => {
console.log("rejected 상태", state, action);
state.isLoading = false;
state.error = action.payload;
},
},
});
export const {} = todosSlice.actions;
export default todosSlice.reducer;
//src/App.js
import React from "react";
import Router from "./shared/Router";
import { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { __addNumber, __minusNumber } from "./redux/modules/counter";
import { __getTodos } from "./redux/modules/todos";
const App = () => {
const dispatch = useDispatch();
const [number, setNumber] = useState(0);
const { isLoading, error, todos } = useSelector((state) => state.todos);
const globalNumber = useSelector((state) => state.counter.number);
//useEffect는 위에 적어줍니다..
useEffect(() => {
dispatch(__getTodos());
}, [dispatch]);
if (isLoading) {
return <div>로딩중</div>;
}
if (error) {
return <div>{error.message}</div>;
}
const onChangeHandler = (e) => {
const { value } = e.target;
setNumber(+value);
};
const onClickAddNumberHandler = () => {
dispatch(__addNumber(number));
};
const onClickMinusNumberHandler = () => {
dispatch(__minusNumber(number));
};
return (
<div>
{todos.map((todo) => {
<div key={todo.id}>{todo.title}</div>;
})}
<div>{globalNumber}</div>
<input type="number" onChange={onChangeHandler} />
<button onClick={onClickAddNumberHandler}>더하기</button>
<button onClick={onClickMinusNumberHandler}>빼기</button>
<Router />
</div>
);
};
export default App;
화면에는 로딩중 이라고 뜬다.