Redux Basic(작성중 입니다.)

Jayden ·2023년 1월 11일
0

1. Action

액션의 타입을 정의하여 변수로 빠는 단계,
액션의 타입은 문자열, 문자열로 넣기에는 실수를 유발할 가능성이 있으므로 미리 정의한 변수를 사용하면, 스펠링에 주의를 덜 기울여도 된다.
하나의 액션 객체를 만들기 위해 하나의 함수를 만들고, 액션의 타입을 미리 정의한 타입 상수로 부터 사용하면 사용 시 오타를 방지할 수 있고 변경이 수월하다.
<action.js>

const ADD_TODO = "ADD_TODO"; // 액션 타입

function addTodo(){
	return{
     	type: ADD_TODO, 
    };
}

전역 state(상태 객체)안에 새로운 todo를 넣는다.
어떤 todo를 넣을 것인지 설정해야 한다.

<actions.js>

export default const ADD_TODO = "ADD_TODO"; // 액션 타입

function addTodo(todo){
	return{
     	type: ADD_TODO,
        todo, //같은 이름이기 때문에 하나만 정의해서 사용해도 된다.
        //todo : todo, 
    };
}

2. Reducer

액션을 주면, 그 액션이 적용되어 달라지는 결과를 만들어주는 함수이다..

Pure Function : 같은 input을 주면 같은 결과를 리턴하는 함수, 리듀서 안에 시간에 따른 달라지는 값이나, 코드가 들어갈 수 없다.

Immutable : orginal state(기존 상태), 새로운 state가 별도의 객체로 만들어야 한다.
reducer를 통해서 state를 인지하는 방식이 Immutable이기 때문에 그렇게 처리되지 않으면 문제가 발생할 수 있음

function 리듀서(previousState, action){
 	return newState; 
}
  • 액션을 받아서 state를 리턴하는 구조
  • 인자로 들어오는 previousState와 리턴되는 newState는 다른 참조를 가지도록 해야합니다.(각각 immutable)
import {ADD_TODO} from "./actions";

//state
//['코딩','점심 먹기'];
function todoApp(previousState, action){
    // 초기값을 설정해 주는 부분
	if(previousState === undefined){
     	return [];   
    }
  
  if(action.type === ADD_TODO){
    	return[...previousState, action.todo];
  }   
  
  
  //previousState.push('');
  
  return previousState;
}

previousState에 대하여 설정을 해주어야 한다. todoApp이 호출되지 않았다면 previousState 상태는 초깃값이기 때문에 초기값을 지정해 주어야 한다.

초기값에 설정을 더 간편하게 하는 법을 살펴보자

import {ADD_TODO} from "./actions";

//state
//['코딩','점심 먹기'];

const initialState = [];

export function todoApp(previousState = initialState , action){
    // 초기값을 설정해 주는 부분
	if(previousState === undefined){
     	return [];   
    }
  
  if(action.type === ADD_TODO){
    	return[...previousState, action.todo];
  }   
  
  
  //previousState.push('');
  
  return previousState;
}

3. Store

스토어를 만드는 함수를 알아보자.

const store = createStore(리듀서);


createStore<S>(1 : reducer:Reducer<S>,2 : preloadedState:S, 3 : enhancer?: StoreEnhancer<S>):Store<S>;

  1. reducer 함수(위에서 정의한 todoApp 같은 함수)

  2. preloadedState : initialstate , 하지만 넣지 않았을 경우 각각의 reducer에서 최초값으로 undefined가 들어오기 때문에 그쪽에서 initialState를 설정할 수 있다.

  3. redux의 기능을 도화주는 미들웨어나 데브툴즈 같은 것들(추후에 설명한다)

<store.js>

import {createStore} from 'redux';
import {todoApp} from "./reducers";
  
const store = createStore(todoApp);
  
export default store;  
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './redux/store';
import {addTodo} from './redux/actions';
  
console.log(store);
console.log(store.getState());
  
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
reportWebVitals();

빈 배열이 나오는 이유는 createStore() 호출시 최초로 실행되면서 todoApp에서 previousState는 undefined 되어서 initialState값으로 되는 [ ]가 설정된다.

store의 상태를 변환 시켜보자.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './redux/store';
import {addTodo} from './redux/actions';
  
console.log(store);
console.log(store.getState());
  
// store.dispatch({type: addTodo, todo}); 이렇게 삽입이 가능하나, 기존에 구현한 action함수를 이용하여 아래와 같이 사용한다.
  
store.dispatch(addTodo("coding"));
console.log(store.getState());
  
  
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
reportWebVitals();
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './redux/store';
import {addTodo} from './redux/actions';


//store의 변경사항 구독 
store.subscribe(()=>{
	console.log(store.getState()); 
})  
 
 
// store.dispatch({type: addTodo, todo}); 이렇게 삽입이 가능하나, 기존에 구현한 action함수를 이용하여 아래와 같이 사용한다.
 
const unsubscribe = store.subscribe(()=>{
 
});

store.dispatch(addTodo("coding"));
store.dispatch(addTodo("read book"));
store.dispatch(addTodo("eat"));

 // console.log(store.getState());

store.dispatch(addTodo("coding"));  // => 실제로 store에 값은 저장된다.
store.dispatch(addTodo("read book"));
store.dispatch(addTodo("eat"));
 
 
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
 <React.StrictMode>
   <App />
 </React.StrictMode>
);
reportWebVitals();

combineReducer

combineReducer가 필요한 이유를 살펴보기 위해 기존 코드를 살펴보도록 하자.

todo를 수행한 경우에 done 처리를 해야하기 때문에 redecer의 state를 변경하도록 하자.

<reducer.js>

import {ADD_TODO} from "./actions";

//state
//['코딩','점심 먹기'];
//[{text: '코딩', done: false}, {text : '점심 먹기', done:true}]   

const initialState = [];

export function todoApp(previousState = initialState , action){
   // 초기값을 설정해 주는 부분
	if(previousState === undefined){
    	return [];   
   }
 
 if(action.type === ADD_TODO){
   	return[...previousState, {text : action.text, done : false}];
 }   
 
 if(action.type === COMPLETE_TODO) {
  		return previousState.map((todo, index) => {
 			if(index === action.index){
  				return {...todo, done: true};
  			}
 		return todo;
  })
  
  
 } 
  
  
 //previousState.push('');
 
 return previousState;
}

<action.js>

export const ADD_TODO = "ADD_TODO";
export const COMPLETE_TODO = "COMPLETE_TODO";  

//{type : ADD_TODO, text : '할일'}  

export fuction addTodo(text){
 	return{
  		type: ADD_TODO,
  		text,
  	};
}  

//{type : COMPLETE_TODO, index : 3}  
export function completeTodo(index){
 	return { 
  		type: COMPLETE_TODO
  		index
  	}
}  
  

<index.js>

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

store.subscribe(()=>{
  	console.log(store.getStore());
});
  
//console.log(store)  
  
store.dispatch(addTodo("할일"));  
store.dispatch(completeTodo(0));  
  
  
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

reportWebVitals();

<reducer.js>

import {ADD_TODO} from "./actions";

//state
//{todos : [{text: '코딩', done: false}, {text : '점심 먹기', done:true}], filter : 'ALL'}
const initialState = {todos : [], filter: 'ALL'}

export function todoApp(previousState = initialState , action){
   // 초기값을 설정해 주는 부분
//	if(previousState === undefined){
//     	return [];   
//    }
 
 if(action.type === ADD_TODO){
  
   	return{
  ...previousState,
  todos : [...previousState.todos, {text : action.text, done : false},};
 }   
 
 if(action.type === COMPLETE_TODO) {
  		return {
  			...previousState
  			todos: previousState.todos.map((todo, index) => {
 			if(index === action.index){
  				return {...todo, done: true};
  			}
 		return todo;
  })}
  
  
 } 
 
  
 if(action.type === SHOW_COMPLETE){
  		return {
  			...previousState,
  			filter : "COMPLETE",
 		} 
 } 
 
 
  if(action.type === SHOW_ALL){
  		return {
  			...previousState,
  			filter : "ALL",
 		} 
 } 
  
  
  
 //previousState.push('');
 
 return previousState;
}

<action.js>

export const ADD_TODO = "ADD_TODO";
export const COMPLETE_TODO = "COMPLETE_TODO";  

//{type : ADD_TODO, text : '할일'}  

export fuction addTodo(text){
 	return{
  		type: ADD_TODO,
  		text,
  	};
}  

//{type : COMPLETE_TODO, index : 3}  
export function completeTodo(index){
 	return { 
  		type: COMPLETE_TODO
  		index
  	}
}  

export const SHOW_ALL = "SHOW_ALL";
export const SHOW_COMPLETE = "SHOW_COMPLETE";

  
export function showAll(){
 	return {type : SHOW_ALL};
}  
  
export function showComplete(){
 	return {type : SHOW_COMPLETE};
}  

<index.js>

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from "./redux/store";
import {addTodo, complete, showComplete} from ./redux/action;  

store.subscribe(()=>{
  	console.log(store.getStore());
});
  
//console.log(store)  
  
store.dispatch(addTodo("할일"));  
store.dispatch(completeTodo(0));
store.dispatch(showComplate(0));  
  
  
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

reportWebVitals();

reducer안에 있는 로직들이 app의 state가 많아질수록 복잡해진다. redux는 단일 store이기 때문에 reducer안에 있는 내용을 분할하는 능력이 필요하다.

다음과 같이 합칠 수 있는데

<reducer.js>

   ```javascript
 import {ADD_TODO, COMPLETE_TODO, SHOW_ALL, SHOW_COMPLETE} from "./actions";
 import {combineReducers} from "redux"; 

//state
//{todos : [{text: '코딩', done: false}, {text : '점심 먹기', done:true}], filter : 'ALL'}
  
const initialState = {todos : [], filter: 'ALL'}

const todosInitialState = initialState.todos;
const fileterInitialState = initialState.filter;  
  
  
const reducer = combineReducers({
  	todos: todosReducer,
    filter : filterReducer
})  
  
export default reducer;  
  
function todoReducer(previousState = todosInitialState , action){
  
  if(action.type === ADD_TODO){
   
   return[...previousState, {text : action.text, done : false}];
  
  
  if(action.type === COMPLETE_TODO) {
   		return previousState.map((todo, index) => {
  			if(index === action.index){
   				return {...todo, done: true};
   			}
  		return todo;
   });
   
   
  } 
  //previousState.push('');
  
  return previousState;
}
  
 function filterReducer(previousState = initialState , action){

  
  if(action.type === SHOW_COMPLETE){
   		return "COMPLETE";
  } 
  
  
   if(action.type === SHOW_ALL){
   		return  "ALL";
  } 
   
   
  //previousState.push('');
  
  return previousState;
} 

<store.js>

 import {createStore} from "redux";
 import todoApp from "./reducers";
 
 const store = createStore(todoApp);
 
 export default store;

폴더를 만들어서(redux/reduce/)

<todos.js>

import{ADD_TODO, COMPLETE_TODO} from "../action/todos  
  
const initialState = [];  
  
function todoReducer(previousState = initialState , action){
  
  if(action.type === ADD_TODO){
   
   return[...previousState, {text : action.text, done : false}];
  
  
  if(action.type === COMPLETE_TODO) {
   		return previousState.map((todo, index) => {
  			if(index === action.index){
   				return {...todo, done: true};
   			}
  		return todo;
   });
   
   
  } 
  //previousState.push('');
  
  return previousState;
}  
  
<filter.js>
```javascript
  import {SHOW_COMPLETE, SHOW_ALL} from '../actions/filter'
  
  const initialState = 'ALL'
  
  function filter(previousState = initialState , action){

  
  
  if(action.type === SHOW_COMPLETE){
   		return "COMPLETE";
  } 
  
  
   if(action.type === SHOW_ALL){
   		return  "ALL";
  }
   
   
  //previousState.push('');
  
  return previousState;
} 

<reducer.js>

import {combineReducers} from "redux";
import todos from './todos';
import filter from './filter';  
  
const reducer = combineReducers({
  	todos,
    filter,
})  
  
export default reducer;  

Redux를 react에 연결하기

단일 store를 만들고, subscribe와 get State를 이용하여 변경되는 state 데이터를 얻어, props로 계속 아래로 전달

  • componentDidMount - subscribe
  • componentWillunmount - unsubscribe
profile
J-SONE 프론트엔드 개발자

0개의 댓글