react

redux, react-redux (리덕스 실습) : 다크모드 / 일반모드

letsDoDev 2024. 8. 10. 15:55

오늘 실습할 내용

redux, react-redux 로 App.js 에 간단한 다크모드, 일반모드 기능을 구현해볼 것이다.

 

- 프로젝트 경로 : C:\react_redux

- react - app 명 : darkmode

- Node 버전 : 20. 10. 0

- IDE : 인텔리제이

 

- 경로

 

- 1-리액트 앱,  2-리덕스,  3-리액트-리덕스 이 3가지만 설치할 예정

- 1-인텔리제이 터미널로 리액트 프로젝트 설치

npx create-react-app darkmode

 

- 제대로 설치되었는지 npm start 로 테스트

npm start

정상동작 확인했으면 control + c -> y 로 진행 중인 프로젝트 종료시키고 남은 라이브러리 마저 설치

 

- 2-리덕스(redux) 설치

npm install --save redux

 

- 3- 리액트-리덕스 설치(react-redux)

npm install --save react-redux

 

- ★ src/ 경로에 actions 디렉토리와 reducers 디렉토리를 만들어주자

   : 개인 학습 겸 업로드된 게시물이기 때문에 이론적인 설명이 많이 부족할 수 있음 주의 ㅠㅠ

src/actions  의 용도 -> 리덕스state(전역변수)를 변경시키는 리덕스 함수에 들어갈 파라미터 js 파일 경로 (해당 경로네 js 파일은 리덕스함수의 파라미터를 유사 프로퍼티처럼 사용하기 위함)

src/reducers 의 용도 -> 리덕스state 와 리덕스 함수를 정의하는 js 파일 경로

 

- src/actions/index.js 파일 생성 및 정의 (리덕스 함수에 들어갈 파라미터 정의)

- src/ actions/ index.js

export const DARK = "DARK"; // 다크모드 변수 : 유사 프로퍼티
export const NORMAL = "NORMAL"; // 일반모드 변수 : 유사 프로퍼티

export const getDarkMode = () => { // 다크모드 변수 값 가져오는 함수 정의
    return {
        type : DARK
    }
}
export  const getNormalMode = () => { // 일반모드 변수 값 가져온느 변수 정의
    return {
        type : NORMAL
    }
}

 

 

- src/reducers/ 경로에 index.js 파일 생성 -> 리덕스 상태 정의 및 리덕스 함수 정의할 js 파일

- src/ reducers/ index.js

import {combineReducers} from "redux";
import {DARK, getDarkMode, getNormalMode, NORMAL} from "../actions";

const initState ={ // 최초 리덕스 state 정의
    mode : NORMAL // default state 는 일반모드
};

/**
 * @param state
 * @param action
 * 파라미터로 받은 action 값이 DARK 냐  NOLMAL 이냐에 따라
 * @returns mode
 */

const data = (state = initState, action) => {
    if(action.type === DARK) { // switch 문 써도 됨.
        return state, {
            mode : getDarkMode().type
        }
    }
    else if(action.type === NORMAL) {
        return state, {
            mode : getNormalMode().type
        }
    } else {
        return state, {
            mode : getNormalMode().type
        }
    }
}

const App = combineReducers({ // combinReducers 사용하여 하나의 큰 리듀서로 묶어주는 작업
    data
});

export default App;

 


- 이제 간단한 다크모드 ,일반모드 테스트를 진행하기 위해  src/ index.js 와 App.js 파일을 수정

- src/ index.js 

   == 리덕스 스테이트와 함수가 저장되고 보관되는 곳을 store 라고 한다.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {createStore} from "redux";
import reducers from "./reducers"; // 리덕스 스테이스, 함수 임포트 dl

// 모든 하위 컴포넌트에서 store 에 접근가능하도록 사용
// react-redux 설치 안 하고 그냥 redux만 설치하는 경우
// 자식 컴포넌트에 props로 store를 계속 넘겨주어야 하는 번거로움이 생기는데 react-redux의 Provider를 사용하면 이런 문제는 간단히 해결된다
import {Provider} from "react-redux";

const store = createStore(reducers) // src/reducers 하위에 있는 모든 js (리덕스 state 와 리덕스 함수를 이제부터 스토어로 관리하겠다는 뜻)

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
    <React.StrictMode>
        <Provider store={store}>
            <App />
        </Provider>
    </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

- 이제 다크모드 / 일반모드를 확인해볼 뷰를 App.js 에다가 바로 작성해서 테스트 해보자

- App.js 수정

import logo from './logo.svg';
import './App.css';
import {DARK, getDarkMode, getNormalMode, NORMAL} from "./actions";
import {useEffect, useState} from "react";
import {useDispatch, useSelector} from "react-redux";
import {combineReducers} from "redux";

function App(props) {
    const dispatch = useDispatch();
    const displayMode = useSelector(state => state.data.mode);

    const [backGroundColor, setBackGroundColor] = useState("white");
    const [color, setColor] = useState("black");

    // 분명 상위 src/index.js 에서 store 만들어서 props 로 넘겨줬는데 props.store에 접근을 못한다...
    // 그래서 위에 useSelector 와 useDispatch로 대신했다..
    // const store = props.store; // src/index.js 에서 Provider로 넘겨준 store;
    // 혹시 모르니깐 src/ index.js 의 <Provider store={store}></Provider> 코드 부분은 지우지 말고 남기자

    const changeDisplayMode = (mode) => {
        if(mode == DARK) {
            //store.dispatch(getDarkMode());
            dispatch(getDarkMode());
        }
        if(mode == NORMAL) {
            // store.dispatch(getNormalMode());
            dispatch(getNormalMode());
        }
    }

    const setDisplayByGlobalState = () => {
        console.log(displayMode);
        if (displayMode === 'DARK') {
            setBackGroundColor("black");
            setColor("white");
        } else if (displayMode === 'NORMAL') {
            setBackGroundColor("white");
            setColor("black");
        } else {
            setBackGroundColor("white");
            setColor("black");
        }
    }

    useEffect(() => {
        setDisplayByGlobalState();
    }, []);

    useEffect(() => { // 리덕스 상태 바뀌면 재렌더링
        // console.log(store.getState().data.mode);
        // console.log(displayMode);
        setDisplayByGlobalState();
    }, [displayMode])

  return (
    <div
        className="App"
        style={{display:"table", margin:"auto", textAlign:"center"}}
    >
      <button
        onClick={() => {
            changeDisplayMode(DARK);
        }}>다크모드
      </button>
      <button
        onClick={() =>{
            changeDisplayMode(NORMAL);
        }}>
        일반모드
      </button>
        <div
          style={{
              width:"100%",
              height:"15em",
              border: '1px solid black',
              background: `${backGroundColor}`
          }}>
          <p
            style={{
                color: `${color}`
            }}>
              redux, react-redux 실습 : 다크모드 / 일반모드
          </p>
        </div>
    </div>
  );
}

export default App;

 

- 최초 렌더링 시

 

- 다크모드 클릭 시

 

- 일반모드 클릭 시

 

-- 실습 끝


 

+

원래 Provider 로 props.store로 리덕스 스테이트에 접근하려고 했으나

props 상속이 안 되는 이슈가 발생해 

useSelector 와 useDispatch 사용

 

자세한 내용은 App.js 주석 참고