일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- googleColaboratory
- react오류
- Do it 자바스크립트 + 제이쿼리 입문
- Colaboratory 글자 깨짐
- 인프런
- Concurrently
- node.js 설치
- 따라하며 배우는 노드 리액트 기본 강의
- 타자 게임 만들기
- intllij 내 Bean을 찾지 못해서 발생하는 오류
- props
- DB Browser
- Spring-Framework
- 웹 게임을 만들며 배우는 리액트
- 리액트
- react
- 모두의 파이썬
- 노드에 리액트 추가하기
- intellij
- You are importing createRoot from "react-dom" which is not supported. You should instead import it from "react-dom/client"
- ReactDOM.render is no longer supported in React 18. Use createRoot instead
- 모던자바스크립트
- node.js로 로그인하기
- vs code 내 node
- Python
- JS 개념
- 계산맞추기 게임
- spring-boot
- 자바스크립트
- 거북이 대포 게임
- Today
- Total
프로그래밍 삽질 중
[next.js + @reduxjs/toolkit] createStore에서 createSlice로 적용해보기 본문
[next.js + @reduxjs/toolkit] createStore에서 createSlice로 적용해보기
평부 2022. 10. 13. 16:48
createStore로 강의 내용 : 섹션 2 Redux 연동하기
* 지금의 포스팅은 아래의 'Next.js와 리덕스 설명(로그인, 로그아웃)'의 수정 부분
https://ba-gotocode131.tistory.com/250
* 수정 이유
▶ 기존에 올렸던 자료를 수정하는 글
▶ createStore 이용 시 밑줄이 쳐지면서 아래와 같은 글로 나오면서 @reduxjs/toolkit의 이용방법을 권장함
we recommend using the configurestore method of the @reduxjs/toolkit package, which replaces createStore
실제 변경 시 어떠한 문제점이 있었나
1. userSlice에서 ...state를 사용하지 않고 원하는 값만 변경하려고 함
→ 리덕스 저장소 값을 React 상태로 사용하므로 직접 변경할 수 없고 리듀서가 '순수'하게 만들어졌다는 것을 보관(기록)하기 위해서임(잊지 말기)
2. postSlice.js에서 기존의 addPost reducer를 변경 시 새로운 값(dummyPost)를 추가할 때 원하는 값(post/addPost)가 나오지 않음
→ 강의자분께 직접 질문드림, 정말 감사합니다... 정말 감사합니다....
▶ 무엇이 문제였나? : 처음에 코드를 입력 시 action.payload에 대한 정확한 이해 부족
▶ 데이터를 {email:'값'} 으로 넘기면 action.payload.email이 되고 바로 넘기면 action.payload인데 데이터 자리에 action.payload()로 함수처럼 넣음(당연히 안 됨)
* 참고 : https://www.inflearn.com/questions/603940
//오류난 코드
export const postSlice = createSlice({
name: "post",
initialState,
reducers: {
addPost: (state, action) => {
(state.mainPosts = action.payload(dummyPost)),
(state.mainPosts = [...state.mainPosts]),
(state.postAdded = true);
},
},
});
//수정된 코드
export const postSlice = createSlice({
name: "post",
initialState,
reducers: {
addPost: (state, action) => {
state.mainPosts.push(action.payload),
(state.mainPosts = [...state.mainPosts]),
(state.postAdded = true);
},
},
});
[@reduxjs/toolkit 적용 전 redux]
components 폴더
└ LoginForm.js
└ PostForm.js
pages 폴더
└ _app.js
reducers 폴더
└ index.js
└ post.js
└ user.js
store 폴더
└ configureStore.js
[@reduxjs/toolkit 적용 후 redux]
components 폴더
└ LoginForm.js
└ PostForm.js
pages 폴더
└ _app.js
store 폴더
└ postSlice.js
└ store.js
└ userSlice.js
1. [user.js]와 [userSlice.js] 비교
[user.js]
export const initialState = {
isLoggedIn: false,
me: null,
signUpData: {},
loginData: {},
};
export const loginAction = (data) => {
return {
type: "LOG_IN",
data,
};
};
export const logoutAction = () => {
return {
type: "LOG_OUT",
};
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "LOG_IN":
return {
...state,
isLoggedIn: true,
me: action.data,
};
case "LOG_OUT":
return {
...state,
isLoggedIn: false,
me: null,
};
default:
return state;
}
};
export default reducer;
[userSlice.js]
▶ 타입과 case 부분을 따로 만드는 것이 아닌 한 번에 합쳐줄 수 있음(코드량이 줄어들음)
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
isLoggedIn: false,
me: null,
signUpData: {},
loginData: {},
};
export const userSlice = createSlice({
name: "user",
initialState,
reducers: {
loginAction: (state, action) => ({
...state,
isLoggedIn: true,
me: action.payload,
}),
logoutAction: (state, action) => ({
...state,
isLoggedIn: false,
me: null,
}),
},
});
export const { loginAction, logoutAction } = userSlice.actions;
export default userSlice.reducer;
2. [post.js]와 [postSlice.js] 비교
[post.js]
export const initialState = {
mainPosts: [
{
id: 1,
content: "첫 게시글입니다.",
User: {
id: 1,
nickname: "제로초",
},
Images: [],
Comments: []
},
postAdded: false,
};
const ADD_POST = "ADD_POST";
export const addPost = {
type: ADD_POST,
};
const dummyPost = {
id: 2,
content: "더미데이터입니다.",
User: {
id: 1,
nickname: "제로초",
},
Images: [],
Comments: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case ADD_POST: {
return {
...state,
mainPosts: [dummyPost, ...state.mainPosts],
postAdded: true,
};
}
default: {
return {
...state,
};
}
}
};
[postSlice.js]
▶ 위의 mainPosts를 state.mainPosts로 dummyPost를 action.paylode로 넣음
▶ dummyPost는 components/PostForm.js 에서 진행 됨
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
mainPosts: [
{
id: 1,
content: "첫 게시글입니다.",
User: {
id: 1,
nickname: "제로초",
},
Images: [],
Comments: []
},
imagePaths: [],
postAdded: false,
};
const dummyPost = {
id: 2,
content: "더미데이터입니다.",
User: {
id: 1,
nickname: "제로초",
},
Images: [],
Comments: [],
};
export const postSlice = createSlice({
name: "post",
initialState,
reducers: {
addPost: (state, action) => {
state.mainPosts.push(action.payload),
(state.mainPosts = [...state.mainPosts]),
(state.postAdded = true);
},
},
});
export const { addPost } = postSlice.actions;
export default postSlice.reducer;
2. [store.js]
- postSlice와 userSlice를 합쳐주는 역할
- 여기서 생긴 wrapper로 __app.js에 감싸줌
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
import user from "./userSlice";
import post from "./postSlice";
const rootReducer = combineReducers({
user,
post,
});
const masterReducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
user: user.reducer,
post: post.reducer,
};
return nextState;
} else {
return rootReducer(state, action);
}
};
export const makeStore = () =>
configureStore({
reducer: masterReducer,
});
export const wrapper = createWrapper(makeStore, { debug: true }); //마지막 이 부분
3. _app.js
import React from "react";
import Head from "next/head";
import PropTypes from "prop-types";
import "antd/dist/antd.css";
import { wrapper } from "../store/store";
const NodeBird = ({ Component }) => {
return (
<>
<Head>
<title>NodeBird</title>
</Head>
<Component />
</>
);
};
NodeBird.propTypes = {
Component: PropTypes.elementType.isRequired,
};
export default wrapper.withRedux(NodeBird); //createStore, createSlice 모두 동일하게 적용
4. LoginForm.js
▶ 컴포넌트이므로 화면에 보여주고 싶다면 import LoginForm from "./LoginForm" 해야 함
▶ 최종화면은 pages에서 보여짐
▶ pages/index.js
└ AppLayout.js
└ LoginForm.js
[index.js]
import React from "react";
import { useSelector } from "react-redux";
import AppLayout from "../components/AppLayout";
import PostForm from "../components/PostForm";
import PostCard from "../components/PostCard";
const Home = () => {
const { isLoggedIn } = useSelector((state) => state.user);
const { mainPosts } = useSelector((state) => state.post);
return (
<AppLayout>
{isLoggedIn && <PostForm />}
{mainPosts.map((c) => {
return <PostCard key={c.id} post={c} />;
})}
</AppLayout>
);
};
export default Home;
[AppLayout.js]
import React from "react";
import { useSelector } from "react-redux";
import PropTypes from "prop-types";
import Link from "next/link";
import styled from "styled-components";
import { Menu, Input, Row, Col, Button } from "antd";
const { Search } = Input;
import UserProfile from "./UserProfile";
import LoginForm from "./LoginForm";
import { createGlobalStyle } from "styled-components";
const Global = createGlobalStyle`
.ant-row {
margin-right: 0 !important;
margin-left: 0 !important;
}
.ant-col:first-child {
padding-left: 0 !important;
}
.ant-col:last-child {
padding-right: 0 !important;
}
`;
const SearchInput = styled(Search)`
vertical-align: "middle";
`;
const onSearch = (value) => console.log(value);
const items = [
{ label: <Link href="/">노드버드</Link>, key: "item-1" },
{ label: <Link href="/profile">프로필</Link>, key: "item-2" },
{
label: <SearchInput placeholder="검색하기" onSearch={onSearch} />,
key: "item-3",
},
{ label: <Link href="/signup">회원가입</Link>, key: "item-4" },
];
const AppLayout = ({ children }) => {
const { isLoggedIn } = useSelector((state) => state.user);
console.log("isLoggedIn", isLoggedIn);
return (
<div>
<Global />
<Menu items={items} mode="horizontal" />
<Row gutter={8}>
<Col xs={24} md={6}>
{isLoggedIn ? <UserProfile /> : <LoginForm />}
왼쪽 메뉴
</Col>
<Col xs={24} md={12}>
{children}
</Col>
<Col xs={24} md={6}>
<Button
type="link"
href="https://ba-gotocode131.tistory.com/"
target="_blank"
rel="noopener noreferrer"
>
블로그
</Button>
</Col>
</Row>
</div>
);
};
AppLayout.prototype = {
children: PropTypes.node.isRequired,
};
export default AppLayout;
[LoginForm.js]
▶ loginAction을 불러옴
▶ dispatch를 이용해 action을 발생시키고 상태를 업데이트 하기 위함
▶ 상태를 업데이트 하는 유일한 방법은 store.dispatch()를 부르고 action 객체를 넘겨주는 것
▶ store은 reducer function을 실행시키고 새로운 state를 내부에 저장할 것
dispatch 참고 : https://developerntraveler.tistory.com/144
import React, { useCallback } from "react";
import { Form, Input, Button } from "antd";
import Link from "next/link";
import styled from "styled-components";
import useInput from "../hooks/useInput";
import { useDispatch } from "react-redux";
import { loginAction } from "../store/userSlice";
const ButtonWrapper = styled.div`
margin-top: 10px;
`;
const FormWrapper = styled(Form)`
padding: 10px;
`;
const LoginForm = () => {
const dispatch = useDispatch();
const [id, onChangeId] = useInput("");
const [password, onChangePassword] = useInput("");
const onSubmitForm = useCallback(() => {
console.log(id, password);
dispatch(
loginAction({
id,
password,
})
);
}, [id, password]);
return (
<FormWrapper onFinish={onSubmitForm} style={{ padding: "10px" }}>
<div>
<label htmlFor="user-id">아이디</label>
<br />
<Input name="user-id" value={id} onChange={onChangeId} required />
</div>
<div>
<label htmlFor="user-password">비밀번호</label>
<br />
<Input
name="user-password"
value={password}
onChange={onChangePassword}
type="password"
required
/>
</div>
<div style={{ marginTop: "10px" }}>
<Button type="primary" htmlType="submit" loading={false}>
로그인
</Button>
<Link href="/signup">
<a>
<ButtonWrapper>회원가입</ButtonWrapper>
</a>
</Link>
</div>
</FormWrapper>
);
};
export default LoginForm;
5. 결과
'과거 프로그래밍 자료들 > React' 카테고리의 다른 글
(next.js + redux toolkit 시리즈 3) reduxjs/toolkit (0) | 2022.10.12 |
---|---|
(next.js + redux toolkit 시리즈 2) next-redux-wrapper (yotube 강의) (0) | 2022.10.12 |
(next.js + redux toolkit 시리즈 1) redux 설명(yotube 강의) (0) | 2022.10.12 |
Next.js와 리덕스 설명(로그인, 로그아웃) (0) | 2022.10.06 |
인라인 스타일링 시 리렌더링 문제(styled-components를 사용하는 이유) (1) | 2022.10.04 |