Redux | Review Redux (with Vanilla JS & React)


Reduxをプロジェクトに適用する前に,簡単なコードで復習した.
(Vanilla JS+Reduxの組み合わせもReduxを理解するのに役立ちます.)

1. Redux - Vanilla JS


Counter


  • index.html
  •   <body>
        <button id="add">Add</button>
        <span></span>
        <button id="minus">Minus</button>
      </body>
  • index.js
  • import { createStore } from "redux";
    
    const add = document.getElementById("add");
    const minus = document.getElementById("minus");
    const number = document.querySelector("span");
    number.innerText = 0;
    
    // 🌟 reducer is func and 🌟 ONLY Reducer can control(modify) datas(states)!
    // 🌟 reducer가 return하는건 app의 state(data)이다, 여야한다.
    // (currnet state or initial state, action)
    
    //2. 리듀서 생성
    const countModifier = (count = 0, action) => {
      //5. 입력받은 action객체의 타입에 따라 state를 조작하여 리턴
      switch (action.type) {
        case "ADD":
          return count + 1;
        case "MINUS":
          return count - 1;
        default:
          return count;
      }
    };
    
    //1. 스토어 생성 후
    //3. 생성된 reducer를 전달
    const countStore = createStore(countModifier);
    
    const onChange = () => {
      number.innerText = countStore.getState();
    };
    countStore.subscribe(onChange);
    
    //4.reducer의 두번째 인자로 들어가는 action객체는 disaptch를 통해서 reducer로 전달!
    //action은 오브젝트 이다! 항상 "type"을 가져야하고, 그 키 값은 바뀔 수 없다.
    
    
    const handleAdd = () => {
      countStore.dispatch({ type: "ADD" });
    };
    
    const handleMinus = () => {
      countStore.dispatch({ type: "MINUS" });
    };
    
    add.addEventListener("click", handleAdd);
    minus.addEventListener("click", handleMinus);
    
    // type을 const ADD = "ADD" 으로 정의하고 사용하게된다면 오류가 났을 때 정확히 판단할 수 있을 것이다.
    //(js가 오류를 어디서 오류가 났는지 알려줄거야...)

    To Do Lists - Vanilla JS


  • index.html
  • <form>
          <input type="text" placeholder="Write to do" />
          <button>Add</button>
        </form>
        <ul></ul>
  • index.js
  • //// To Dos ////
    
    const form = document.querySelector("form");
    const input = document.querySelector("input");
    const ul = document.querySelector("ul");
    
    //action type
    const ADD_TODO = "ADD_TODO";
    const DELETE_TODO = "DELETE_TODO";
    
    //action creator
    const addToDo = text => {
      return { type: ADD_TODO, text };
    };
    const deleteToDo = id => {
      return { type: DELETE_TODO, id };
    };
    
    //reducer
    // mutate -> X create -> O
    const reducer = (state = [], action) => {
      // console.log(action);
      switch (action.type) {
        case ADD_TODO:
          //새로운 todo 오브젝트 생성
          const newToDoObj = { text: action.text, id: Date.now() };
          return [newToDoObj, ...state];
        case DELETE_TODO:
          // filter을 이용하여 state를 변경하는 것이 아닌 새로운 배열을 리턴한다
          return state.filter(toDo => toDo.id !== action.id);
        default:
          return state;
      }
    };
    
    //store생성
    const store = createStore(reducer);
    
    //dispatch
    const dispatchAddToDo = text => {
      store.dispatch(addToDo(text));
    };
    const dispatchDeleteToDo = e => {
      const id = parseInt(e.target.parentNode.id);
      store.dispatch(deleteToDo(id));
    };
    
    //subscribe의 listener
    const paintToDos = () => {
      const toDos = store.getState();
      ul.innerHTML = "";
      toDos.forEach(toDo => {
        const li = document.createElement("li");
        const btn = document.createElement("button");
        btn.innerText = "delete";
        btn.addEventListener("click", dispatchDeleteToDo);
        li.id = toDo.id;
        li.innerText = toDo.text;
        li.appendChild(btn);
        ul.appendChild(li);
      });
    };
    
    //subscribe
    store.subscribe(paintToDos);
    
    const onSubmit = e => {
      e.preventDefault();
      const toDo = input.value;
      input.value = "";
      dispatchAddToDo(toDo);
    };
    
    form.addEventListener("submit", onSubmit);

    2. Redux - React


    To Do Lists - React


  • index.js
  • //// React Redux
    ReactDOM.render(
      <Provider store={store}>
        <App></App>
      </Provider>,
      document.getElementById("root")
    );
  • home.jsx
  • const Home = ({ toDos, addToDo }) => {
      const formRef = React.createRef();
      const inputRef = React.createRef();
    
      const onSubmit = event => {
        event.preventDefault();
        const text = inputRef.current.value;
        text && addToDo(text);
        formRef.current.reset();
      };
    
      return (
        <>
          <h1>To Dos </h1>
          <form ref={formRef} onSubmit={onSubmit}>
            <input ref={inputRef} type="text" />
            <button>Add</button>
          </form>
          <ToDos toDos={toDos} />
        </>
      );
    };
    
    //순수함수, 동기적
    const mapStateToProps = (state, ownProps) => {
      return { toDos: state };
    };
    
    const mapDispatchToProps = dispatch => {
      return {
        addToDo: text => dispatch(actionCreater.addToDo(text)),
      };
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Home);
  • todos.jsx
  • const ToDos = ({ toDos }) => {
      return (
        <ul>
          {toDos.map(toDo => {
            return <List toDo={toDo} key={toDo.id} />;
          })}
        </ul>
      );
    };
    
    export default ToDos;
    
  • list.jsx
  • const List = ({ toDo, deleteToDo }) => {
      const deleteList = () => {
        deleteToDo(toDo.id);
      };
    
      return (
        <li>
          {toDo.text}
          <button onClick={deleteList}>delete</button>
        </li>
      );
    };
    
    const mapDispatchToProps = dispatch => {
      return {
        deleteToDo: id => dispatch(actionCreater.deleteToDo(id)),
      };
    };
    
    export default connect(null, mapDispatchToProps)(List);