redux 사용 시 createStore에 취소선이 그어져있다는 제보를 해주시는 분들이 많습니다.
결론적으로는 createStore 그대로 쓰셔도 됩니다. 아무 문제 없습니다.
다만 redux 팀에서는 이제 redux를 쓰지 말고 @reduxjs/toolkit을 쓰라는 것으로 공식 입장을 정한 것 같습니다.
툴킷 적용을 원하신다면 제 깃헙 toolkit 브랜치 참고하시면 되겠습니다.
아래의 패키지 설치
npm i next-redux-wrapper@6
npm i redux
npm i react-redux@7
store폴더에 configureStore.js파일을 생성함
// store/configureStore.js
import { createWrapper } from "next-redux-wrapper";
import { createStore } from "redux";
const configureStore = () => {
const store = createStore(reducer);
return store;
};
// debug툴은 개발모드에서는 true로 놓고 쓰는 편이 코딩할 때 편함
const wrapper = createWrapper(configureStore, {
debug: process.env.NODE_ENV === "development",
});
export default wrapper;
_app.js
import wrapper from "../store/configureStore";
...
export default wrapper.withRedux(NodeBird);
{
..state,
name: action.data,
}
// reducer/index.js
const rootReducer = (state, action) => {
switch (action.type) {
}
};
export default rootReducer;
// configureStore.js
import reducer from "../reducers";
const configureStore = () => {
const store = createStore(reducer);
return store;
};
// reducer/index.js
const initialState = {
name: "zerocho",
age: 27,
password: "babo",
};
const changeNickname = {
type: "CHANGE_NICKNAME",
data: "boogicho",
};
// (이전상태 ,액션) => 다음상태
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case "CHANGE_NICKNAME":
return {
...state,
name: action.data,
};
}
};
// 이전
const changeNickname = {
type: "CHANGE_NICKNAME",
data: "boogicho",
};
// 변경
const changeNickname = (data) => {
return {
type: 'CHANGE_NICKNAME',
data,
}
}
changeNickname('zerocho');
// reducers/index.js
const initialState = {
user: {
isLoggedIn: false,
user: null,
signUpData: {},
loginData: {},
},
post: {
mainPosts: [],
},
};
export const loginAction = (data) => {
return {
type: "LOG_IN",
data,
};
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case "LOG_IN":
return {
...state,
user: {
...state.user,
isLoggedIn: true,
user: action.data,
},
};
}
};
// AppLayout.js
import { useSelector } from "react-redux";
const AppLayout = ({ children }) => {
const isLoggedIn = useSelector((state) => state.user.isLoddedIn);
...
// LoginForm.js
import { useDispatch } from "react-redux";
const LoginForm = () => {
const dispatch = useDispatch();
...
const onSubmitForm = useCallback(() => {
console.log(id, password);
dispatch(loginAction({ id, password }));
}, [id, password]);
npm i redux-devtools-extension
// store/configureStore.js
import { createStore, applyMiddleware, compose } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
const configureStore = () => {
const middlewares = [];
const enhancer =
process.env.NODE_ENV === "production"
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(applyMiddleware(...middlewares));
const store = createStore(reducer, enhancer);
return store;
};
...
// reducers/index.js
const initialState = {
user: {
isLoggedIn: false,
user: null,
signUpData: {},
loginData: {},
},
post: {
mainPosts: [],
},
};
export const loginAction = (data) => {
return {
type: "LOG_IN",
data,
};
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case "LOG_IN":
return {
...state,
user: {
...state.user,
isLoggedIn: true,
user: action.data,
},
};
}
};
// user.js
export const initialState = {
isLoggedIn: false,
user: 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,
user: action.data,
};
case "LOG_OUT":
return {
...state,
isLoggedIn: false,
user: null,
};
default:
return state;
}
};
export default reducer;
// post.js
export const initialState = {
mainPosts: [],
};
const reducer = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
export default reducer;
// reducers/index.js
import { HYDRATE } from "next-redux-wrapper";
import { combineReducers } from "redux";
import user from "./user";
import post from "./post";
const rootReducer = combineReducers({
index: (state = {}, action) => {
switch (action.type) {
case HYDRATE:
return { ...state, ...action.payload };
default:
return state;
}
},
user,
post,
});
const isLoggedIn = useSelector((state) => state.user.isLoggedIn);
// 구조분해해도 동일하게 작동함
const { isLoggedIn } = useSelector((state) => state.user);
export const initialState = {
mainPosts: [
{
id: 1,
User: {
id: 1,
nickname: "BonnieC",
},
content: "첫 번째 게시글 #해시태그 #익스프레스",
Images: [
{
src: "https://user-images.githubusercontent.com/68591616/197114835-ac101ba7-4271-433d-a4c1-1be37b008f13.jpg",
},
{
src: "https://user-images.githubusercontent.com/68591616/197114859-85dc51ce-cb85-4125-b635-3eb791706dc2.jpg",
},
{
src: "https://user-images.githubusercontent.com/68591616/197114881-824baadb-4fd2-4c28-8f8f-df12e374bdfc.jpg",
},
],
Comments: [
{
User: {
nickname: "nero",
},
content: "우와 개정판이 나왔군요~",
},
{
User: {
nickname: "hero",
},
content: "얼른 사고 싶어요~",
},
],
},
],
};
const ADD_POST = "ADD_POST";
export const addPost = {
type: ADD_POST,
};
...
const onClickImageUpload = useCallback(() => {
imageInput.current.click();
}, [imageInput.current]);
...
...
<input type="file" multiple hidden ref={imageInput} />
<Button onClick={onClickImageUpload}>이미지 업로드</Button>
PostCard.propTypes = {
post: PropTypes.object.isRequired,
};
// 아래와 같이 풀어서 쓸 수도 있음
PostCard.propTypes = {
post: PropTypes.shape({
id: PropTypes.number,
User: PropTypes.object,
content: PropTypes.string,
createdAt: PropTypes.object,
Comments: PropTypes.arrayOf(PropTypes.object),
Images: PropTypes.arrayOf(PropTypes.object),
}).isRequired,
};
setLiked((prev) => !prev);
npm i react-slick
const Overlay = styled.div``;
const Global = createGlobalStyle`
.slick-slide {
display: inline-block;
}
`;
...
return (
<Overlay>
<Global />
<Header>
...
// styles.js
import styled, { createGlobalStyle } from "styled-components";
import { CloseOutlined } from "@ant-design/icons";
export const Overlay = styled.div`
position: fixed;
z-index: 5000;
top: 0;
left: 0;
right: 0;
bottom: 0;
`;
// index.js
import React, { useState } from "react";
import PropTypes from "prop-types";
import Slick from "react-slick";
import { Overlay } from "./styles";
...
"scripts": {
"dev": "next -p 3060"
},
...