리엑트 상태관리 정리

이봐요이상해씨·2021년 12월 31일
0

리엑트

목록 보기
2/2
  1. 리덕스 호출 > 리듀서를 호출한다.

    const reducer = (state, action) => {}
  2. store에 action을 전달하는 방법 > dispatch를 통해 store에 전달

    action은 object형태로 전달되어야 함으로 {type : " " } 형태로 전달되어야 한다.

    store.dispatch({type:"object"})

즉 message를 dispatch를 통해 store에 보내는데, 그 message는 action이 되는 것이고 그 action은 reducer에 의해 행해지는 것 그리고 subscribe을 통해 store안에 있는 변화를 알아낼 수 있음

import {createStore} from 'redux'

//데이터를 수정하는 함수 (reducer)
const countModifier = (count = 0, action) => {
  if (action.type == "ADD") {
    return count+1;
  } else if (action.type === "MINUS"){
    return count -1;
  }else{
    return count;
  }
}

const countStore = createStore(countModifier);

//데이터 변조를 위해 countModifier(reducer)에 메시지를(action)을 dispatch를 통해 보냄 
countStore.dispatch({type:"ADD"})
countStore.dispatch({type:"ADD"})
countStore.dispatch({type:"ADD"})
countStore.dispatch({type:"ADD"})
countStore.dispatch({type:"MINUS"})

console.log(countStore.getState())
import {createStore} from 'redux'

//오타 방지를 위해 action type을 한곳에 정의

const ADD = "ADD";
const MINUS = "MINUS"

const add = document.getElementById("add");
const minus = document.getElementById("minus");
const number = document.querySelector("span");

let count = 0

number.innerText = count;

//reducer 는 함수이며, data를 수정한다
//createstore는 인자로 reducer를 요구한다
//여기서는 count 를 수정함
//return 하는 것은 너의 data를 리턴
const countModifier = (count, action) => {

  //if else문을 switch로 변경
  switch (action.type) {
    case ADD:
      return count + 1
    case MINUS:
      return count -1
    default:
      return count;
  }

//action => count++, count-- 를 해주는 데이터를 변경시켜주는 형태
  // if (action.type == "ADD") {
  //   return count+1;
  // } else if (action.type === "MINUS"){
  //   return count -1;
  // }else{
  //   return count;
  // }
}

//createStore => state를 넣는 창고
//여기서 state ==  count 부분!!(바뀌는 부분)
//초기화된 값과 action을 불러온다.
const countStore = createStore(countModifier);

const onChange = () => {
  console.log("change!!");

}

//subscribe을 통해 store내에 변화가 있으면 감지함
countStore.subscribe(onChange);

//이런식으로 써도 되고 아니면 아래 MINUs처럼 써도 된다(버튼을 연결하는 방법)
const handleAdd = () => {
  countStore.dispatch({type:ADD})
}

//dispatch 를 버튼과 연결 
add.addEventListener("click", handleAdd);
minus.addEventListener("click", () => countStore.dispatch({type:MINUS}));

//action을 modifier에 호출하는 방법 -> dispatch 를 사용 
// countStore.dispatch({type:"ADD"})
// countStore.dispatch({type:"ADD"})
// countStore.dispatch({type:"ADD"})
// countStore.dispatch({type:"ADD"})
// countStore.dispatch({type:"MINUS"})

console.log(countStore.getState())

TODO

vanila

const form = document.querySelector("form");
const input = document.querySelector("input");
const ul = document.querySelector("ul");

const createToDo = toDo => {
    const li = document.createElement("li");
    li.innerText = toDo;
    ul.appendChild(li);
};

const onSubmit = e => {
    e.preventDefault();
    const toDo = input.value;
    input.value = "";
    createToDo(toDo);
}

form.addEventListener("submit", onSubmit);

주의 할점

  1. store값을 변경하는 방법은 dispatch를 통한 방법 밖에 없다.
  2. state를 변경하지마라 ⇒ 새로운 state를 리턴 해라!!
const reducer = (state = [], action) => {
  console.log(action);
  switch (action.type) {
    case ADD_TODO:
      return []; //배열로서 반환 
    case DELETE_TODO:
      return [];
    default:
      return state;
  }
};

여기서

const reducer = (state = [], action) => {
  console.log(action);
  switch (action.type) {
    case ADD_TODO:
      state.push("sss") // 이런식으로 변경하지 말라는 뜻 
    case DELETE_TODO:
      return [];
    default:
      return state;
  }
};

대신에 이렇게 해라

const reducer = (state = [], action) => {
  console.log(action);
  switch (action.type) {
    case ADD_TODO:
      return [...state, {text:action.text}] // 새로운 object를 리턴해라 
    case DELETE_TODO:
      return [];
    default:
      return state;
  }
};

TODO(Redux + Valnila JS)

import { createStore } from "redux";

const form = document.querySelector("form");
const input = document.querySelector("input");
const ul = document.querySelector("ul");

const ADD_TODO = "ADD_TODO";
const DELETE_TODO = "DELETE_TODO";

//action type을 반환하는 객체 -> actionCreator
const addToDo = text => {
  return {
      type : ADD_TODO,
      text
  };
};

const deleteTodo = id => {
  return {
      type : DELETE_TODO,
      id
  };
};

//리듀서부분
const reducer = (state = [], action) => {
  console.log(action);
  switch (action.type) {
    case ADD_TODO:
        //리펙토링 부분 
        const newTodoObj = {text: action.text, id : Date.now()}
      return [newTodoObj, ...state];
    case DELETE_TODO:
        const cleaned = state.filter(toDo => toDo.id !== action.id);
      return cleaned;
    default:
      return state;
  }
};

//스토어 생성
const store = createStore(reducer);
//스토어 구독
store.subscribe(() => console.log(store.getState()));

//디스패치 부분
const dispatchAddToDo = text => {
  store.dispatch(addToDo(text));
};

const dispatchDeleteTodo = e => {
    const id = parseInt(e.target.parentNode.id);
    store.dispatch(deleteTodo(id))
}

//적용함수 
const paintToDos = () => {
  const toDos = store.getState();
  //repaint시 모든것을 다 비우고(list)
  ul.innerHTML = "";

  //state에 있는 각각의 toDo를 이용해서 새로운 list를 만든다 
  toDos.forEach(toDo => {
    const li = document.createElement("li");
    const btn = document.createElement("button");
    btn.innerText = "DEL";
    btn.addEventListener("click", dispatchDeleteTodo)

    li.id = toDo.id;
    li.innerText = toDo.text;

    li.appendChild(btn);
    ul.appendChild(li);

  });
};

store.subscribe(paintToDos);

const onSubmit = e => {
  e.preventDefault();
  const toDo = input.value; //들어온 값
  input.value = "";
  dispatchAddToDo(toDo);
};

form.addEventListener("submit", onSubmit);

TODO (REACT redux only)

STORE

import {createStore} from 'redux';

const ADD = "ADD";
const DELETE = "DELTE";

//action creator
const addToDo = (text) => {
    return {
        type : ADD,
        text
    };
};

 const deleteToDo = (id) => {
    return {
        type : DELETE,
        id : parseInt(id)
    };
};

//리듀서
const reducer = (state = [], action) => {
    switch(action.type) {
        case ADD:
            return [{text: action.text, id : Date.now()}, ...state];
        case DELETE:
            return state.filter(toDo => toDo.id !== action.id);
        default:
            return state;
    }
};

//스토어 생성
const store = createStore(reducer);

export const actionCreatores = {
    addToDo,
    deleteToDo
}

export default store;

COMPONENTS/APP

import React from "react";
import { HashRouter as Router, Route } from "react-router-dom";
import Home from "../routes/Home";
import Detail from "../routes/Detail";

function App() {
  return (
    <Router>
      <Route path="/" exact component={Home}></Route>
      <Route path="/:id" component={Detail}></Route>
    </Router>
  );
}

export default App;

COMPONENTS/TODO

import React from 'react'
//import { actionCreatores } from '../store';
import {remove} from "../store"
import { connect } from "react-redux";
import { Link } from "react-router-dom";

function ToDo({text, onBtnClick, id}) {
    return (
        <li>
            <Link to={`/${id}`}>
                {text}
            </Link>
            <button onClick={onBtnClick}>DEL</button>
        </li>
    )
}

function mapDispatchToProps(dispatch, ownProps){
    return {
        //onBtnClick : () => dispatch(actionCreatores.deleteToDo(ownProps.id)) 
        onBtnClick : () => dispatch(remove(ownProps.id)) 
    }
}

export default connect(null, mapDispatchToProps) (ToDo);
//클릭시 onBtnClick 함수 호출, -> 이 함수는 dispatch로 store.js의 action.creatores의 deleteTod
// -> deleteTodo는  type :DELETE, id : parsein(id)로 이 액션은 reducer로 전달
// -> reducer에서 액션 처리후 filtering된 상태를 반환하고 이 반환값을 ToDO 컴포넌트의 props로 추가함

ROUTES/DETAIL

import React from "react";
import { connect } from "react-redux";

function Detail({ toDo }) {
  return (
    <>
      <h1>{toDo?.text}</h1>
      <h5>Created at: {toDo?.id}</h5>
    </>
  );
}

function mapStateToProps(state, ownProps) {
  const {
    match: {
      params: { id }
    }
  } = ownProps;
  return { toDo: state.find(toDo => toDo.id === parseInt(id)) }; //params의 id 와 같은 첫번쨰 값을 반환 
}

export default connect(mapStateToProps)(Detail);

ROUTES/HOME

import React, { useState } from "react";
import { connect } from "react-redux";
//import { actionCreatores } from "../store";
import {add} from "../store"
import ToDo from '../components/ToDo'

//store의 todos를 갖고옴(state)
//addToDo는 text를 인자로 받아서 store로 dispatch한것
function Home({ toDos, addToDo}) {
  const [text, setText] = useState("");

  function onChange(e) {
    setText(e.target.value);
  }

  function onSubmit(e) {
    e.preventDefault();
    setText("");

    addToDo(text)
  }

  return (
    <>
      <h1>To Do</h1>
      <form onSubmit={onSubmit}>
        <input type="text" value={text} onChange={onChange} />
        <button>Add</button>
      </form>
      <ul>
         {toDos.map(toDo => (
             <ToDo {...toDo} key = {toDo.id} />
         ))}
      </ul>
    </>
  );
}

//App.js의 router를 통해 store.js 의 
//const reducer = (state = [], action) => 
//부분의 state(store의) 와 react-router를 통해 Home으로 전달받은 components의 ownprops를 받음 
// function getCurrentState(state,ownProps)
//  {
//     console.log(state, ownProps);
//  }

//  //connect를 통해서 store.js에서 home으로 state를 갖고올것 이다 
// export default connect (getCurrentState) (Home);
function mapStateToProps(state) {
    return {toDos: state};
}

//home.js에 입력하면, store.js의 reducer로 dispatch 시킨다.
function mapDispatchToProps(dispatch){
//addTodo함수는 dispatch 시킴
    return {
        //addToDo: text => dispatch(actionCreatores.addToDo(text))
        addToDo : text => dispatch(add(text))
    }
}

// connect는 argument로 state와 dispatch를 가진다.
// ● mapStateToProps는 두 종류의 argument와 함께 호출되는 function이다.
// ▷ 첫번째 argument는 Redux store에서 온 state이다.
// ▷ 두번째 argument는 component의 props이다.

// ※connect()는 return한 것을 component의 prop에 추가해준다
export default connect(mapStateToProps, mapDispatchToProps)(Home);

😀 Connect vs dispatch 비교

ToDO.js /connect

import React from 'react'
import { actionCreatores } from '../store';
import { connect, useDispatch } from "react-redux";
import { Link } from "react-router-dom";

function ToDo({text,onBtnClick, id}) 

    return (
        <li>
            <Link to={`/${id}`}>
                {text} <button onClick={onBtnClick}>DEL</button>
            </Link>
        </li>
    )
}

function mapDispatchToProps(dispatch, ownProps){
    return {
        onBtnClick : () => dispatch(actionCreatores.deleteToDo(ownProps.id))  
    }
}

export default connect(null, mapDispatchToProps) (ToDo);

ToDo.js/dispatch

import React from 'react'
import { actionCreatores } from '../store';
import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";

function ToDo({text, id}) {

const dispatch = useDispatch();

const onBtnClick = () => {
	dispatch(actionCreators.deleteToDo(id));
}

    return (
        <li>
            <Link to={`/${id}`}>
                {text} <button onClick={onBtnClick}>DEL</button>
            </Link>
        </li>
    )
}

export default ToDo;

REDUX-TOOLKIT

STORE

import {createStore} from 'redux';
import {createAction,createReducer, configureStore, createSlice} from "@reduxjs/toolkit"

// //toolkit의 creatorAction으로 아래읰 코드들을 대체 
// const addToDo = createAction("ADD")
// const deleteToDo = createAction("DELETE")

// // const ADD = "ADD";
// // const DELETE = "DELTE";

// //action creator
// // const addToDo = (text) => {
// //     return {
// //         type : ADD,
// //         text
// //     };
// // };

// //  const deleteToDo = (id) => {
// //     return {
// //         type : DELETE,
// //         id : parseInt(id)
// //     };
// // };

// //toolkit으로 reducer 만들떈 state를 변경 할 수 있다. 
// //return은 새로운 state를 반환할때 하는 것,
// //toolkit - reducer는  state를 변경하는것이기 때문에 return 하지 않음 

// //현재 state = [] (비어있는 배열 )
// const reducer = createReducer([], {
//     [addToDo] : (state, action) => {
//         state.push({text:action.payload, id: Date.now()}); //push : 비어있는 배열에 새로운 값을 넣어준다.(새로운 배열 리턴 x) 중괄호 감쌈 () => {}
//     },
//     [deleteToDo] : (state, action) => 
//         state.filter(toDo => toDo.id !== action.payload)//filter : 새로운 배열을 리턴함 중괄호 안쌈 () => 
// });

// //리듀서
// // const reducer = (state = [], action) => {
// //     switch(action.type) {
// //         case addToDo.type:
// //             return [{text: action.payload, id : Date.now()}, ...state]; //인자값이 아닌 payload로 전달
// //         case deleteToDo.type:
// //             return state.filter(toDo => toDo.id !== action.payload); //action은 createAction 함수로 만들어지기 때문에 더이상 id를 갖고 있지 않아 payload로 변경 
// //         default:
// //             return state;
// //     }
// // };

//createSlice를 이용하여 reducer와 createAction을 합침
const toDos = createSlice({
    name:'toDosReducer',
    initialState: [],
    reducers: {
        add: (state, action) => {
            state.push({text: action.payload, id: Date.now()});
        },
        remove : (state, action) => state.filter(toDo => toDo.id !== action.payload)
    }

})

const store = configureStore({reducer: toDos.reducer})

//slice에서 action까지 제공해줌 
export const {
    add,
    remove
} = toDos.actions

// const store = configureStore({reducer})

// //스토어 생성
// // const store = createStore(reducer);

// export const actionCreatores = {
//     addToDo,
//     deleteToDo
// }

export default store;

0개의 댓글