
react-redux를 안 쓰고 연결하기단일 store를 만들고, subscribe와 getState를 이용하여,
변경되는 state 데이터를 얻어, props로 계속 아래로 전달
- componentDidMount - subscribe
 - componentWillUnmount - unsubscribe
 
src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './redux/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App store={store} />
  </React.StrictMode>
);
src/App.js
import logo from './logo.svg';
import './App.css';
import { useState, useEffect } from 'react';
import { addTodo } from './redux/actions';
function App({store}) {
  const [state, setState] = useState(store.getState()); // 초기값
  useEffect(()=>{
    const unsubscribe = store.subscribe(()=>{
      setState(store.getState());
    });
    return () => {
      unsubscribe();
    };
  }, [store]); // dependency
  
  // store가 들어올 때, 다른 store면 다시 실행하는 것이므로 검토해보면
  // index.js에서 다른 store를 다시 넣어주는 경우는 없음
  // 결과적으로 1번만 실행
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        {JSON.stringify(state)} {/* 객체를 JSON 문자열로 변환*/}
        <button onClick={click}>추가</button>
      </header>
    </div>
  );
  // 버튼 클릭 시 addTodo 액션
  function click() {
    store.dispatch(addTodo("todo"));
  }
}
export default App;
store만 컴포넌트에서 가지고 있게 되면 store의 변화에 반응하거나
store의 변화에 반응을 줄 수 있는 컴포넌트 생성이 가능하다
src/contexts/ReduxContext.js
import { createContext } from "react";
const ReduxContext = createContext();
export default ReduxContext;
가장 상위에서 ReduxContext를 Provider로 만들고, value로 store를 주입
src/index.js
import ReduxContext from './contexts/ReduxContext';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <ReduxContext.Provider value={store}>
      <App />
    </ReduxContext.Provider>
  </React.StrictMode>
);
store를 context로부터 가져오기
src/App.js
import ReduxContext from './contexts/ReduxContext';
function App() {
  const store = useContext(ReduxContext);
  
  ...
  
공통적인 state에 관한 로직은 따로 custom hook로 뺄 수 있다
src/App.js
import logo from './logo.svg';
import './App.css';
import { useState, useEffect, useContext } from 'react';
import { addTodo } from './redux/actions';
import ReduxContext from './contexts/ReduxContext';
function useReduxState() {
  const store = useContext(ReduxContext);
  const [state, setState] = useState(store.getState());
  useEffect(()=>{
    const unsubscribe = store.subscribe(()=>{
      setState(store.getState());
    });
    return () => {
      unsubscribe();
    };
  }, [store]); 
  return state;
}
 // 공통 로직
// dispatch를 반환하면 그 결과물을 useReduxDispatch로 가져올 수 있음
function useReduxDispatch() {
  const store = useContext(ReduxContext);
  return store.dispatch;
}
function App() {
  // 공통 로직
  // state를 반환하면 그 결과물을 useReudxState로 가져올 수 있음
  const state = useReduxState();
  const dispatch = useReduxDispatch();
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        {JSON.stringify(state)}
        <button onClick={click}>추가</button>
      </header>
    </div>
  );
  // 버튼 클릭 시 addTodo 액션
  // dispatch
  function click() {
    dispatch(addTodo("todo"));
  }
}
export default App;
결과
App 컴포넌트 안에 하위 컴포넌트 2개 추가하기
- src/components/TodoList.jsx
 - src/components/TodoForm.jsx
 
src/App.js
import TodoList from './components/TodoList';
import TodoForm from './components/TodoForm';
...
function App() {
  const state = useReduxState();
  const dispatch = useReduxDispatch();
  return (
    <div className="App">
      <TodoList />
      <TodoForm />
    </div>
  );
  
  ...
  
 }
 
 ...
TodoList 만들기 전에 만든 Hook들을 빼내서 범용적으로 사용할 수 있게 하기
- src/hooks/useReduxState.js
 - src/hooks/useReduxDispatch.js
 => src/App.js에 있는 useReduxState와 useReduxDisptch 옮겨주기
src/hooks/useReduxState.js
import { useState, useEffect, useContext } from 'react';
import ReduxContext from '../contexts/ReduxContext';
export default function useReduxState() {
  const store = useContext(ReduxContext);
  const [state, setState] = useState(store.getState());
  useEffect(()=>{
    const unsubscribe = store.subscribe(()=>{
      setState(store.getState());
    });
    return () => {
      unsubscribe();
    };
  }, [store]); 
  
  return state;
}
src/hooks/useReduxDispatch.js
import { useContext } from 'react';
import ReduxContext from '../contexts/ReduxContext';
export default function useReduxDispatch() {
  const store = useContext(ReduxContext);
  return store.dispatch;
}
state와 dispatch는 hooks를 import해서 사용
src/App.js
import { addTodo } from './redux/actions';
import useReduxState from './hooks/useReduxState';
import useReduxDispatch from './hooks/useReduxDispatch';
import TodoList from './components/TodoList';
import TodoForm from './components/TodoForm';
function App() {
  const state = useReduxState();
  const dispatch = useReduxDispatch();
  
  ...
  
- TodoList : 보여주는 역할 (useReduxState, map)
 - TodoForm : 추가하는 역할 (useReduxDispatch, useRef, addTodo)
 - App.js : 렌더만 하는 역할 (위 컴포넌트들 생성 후 App.js에 중복되는 내용 삭제)
 
src/components/TodoList.jsx
import useReduxState from '../hooks/useReduxState';
export default function TodoList() {
  const state = useReduxState();
  return (
    <ul>
      {state.todos.map((todo)=>{
        return <li>{todo.text}</li>;
      })}
    </ul>
  );
}
src/components/TodoForm.jsx
import { useRef } from "react";
import { addTodo } from "../redux/actions";
import useReduxDispatch from './../hooks/useReduxDispatch';
// uncontrolled components
export default function TodoForm() {
  const inputRef = useRef();
  const dispatch = useReduxDispatch();
  return (
    <div>
      <input ref={inputRef} /> <button onClick={click}>추가</button>
    </div>
  );
  function click() {
    dispatch(addTodo(inputRef.current.value));
  }
}
src/App.js
import logo from './logo.svg';
import './App.css';
import TodoList from './components/TodoList';
import TodoForm from './components/TodoForm';
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <TodoList />
        <TodoForm />
      </header>
    </div>
  );
}
export default App;
결과