생활코딩! React 리액트 프로그래밍

egoing - React redux - stackblitz ver

lshjju 2025. 9. 6. 16:20

React redux

https://lshjju.tistory.com/150

 

React.js - redux - 리액트 리덕스

Redux(리덕스)란 무엇인가요? Redux는 React(뿐만 아니라 다른 JavaScript 라이브러리/프레임워크)에서 **애플리케이션의 상태(State)를 효율적으로 관리하기 위한 예측 가능한 상태 컨테이너(Predictable Stat

lshjju.tistory.com



Final build

https://stackblitz.com/edit/vitejs-vite-75rbaovk?file=src%2FApp.jsx

 

egoing - React redux - StackBlitz

Next generation frontend tooling. It's fast!

stackblitz.com



New project building

https://lshjju.tistory.com/104

 

StackBlitz web editor - 스택블리츠 웹 에디터

스택블리츠로 새 프로젝트 생성하기 https://stackblitz.com/ 로그인 방법 선택 대쉬보드에서 new project + 버튼 - 탭 모달 - frontend 탭 - 탭react javascript - 탭 바이트 리액트가 생성됩니다.프로젝트 생성 완

lshjju.tistory.com



시뻘건 박스 몇개 만들어 봅니다.

 


#container,
#container div {
  border: 5px solid red;
  margin: 10px;
}

 

App.css

 

일단 다 지웁니다.

그리고 간단한 스타일을 제작 합니다.


export default function App() {
  return (
    <div id="container">
      <h1>Root</h1>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1</h1>
    </div>
  );
}

 

펑션레프트1을 추가 합니다.


export default function App() {
  return (
    <div id="container">
      <h1>Root</h1>
      <Left1></Left1>
    </div>
  );
}

 

펑션앱에 레프트를 추가하고 스타일을 적용합니다.

// 'App'이라는 이름의 함수형 컴포넌트를 정의하고 기본 내보내기로 설정합니다.
// 이 컴포넌트가 애플리케이션의 가장 상위(root) 컴포넌트 역할을 합니다.
export default function App() {
  // 컴포넌트가 렌더링할 UI를 정의하는 JSX를 반환합니다.
  return (
    // 'container'라는 ID를 가진 div 요소입니다.
    <div id="container">
      {/* "Root"라는 텍스트를 표시하는 제목입니다. */}
      <h1>Root</h1>
      {/* 'Left1' 컴포넌트를 렌더링합니다. React의 컴포넌트 합성(composition)을 보여줍니다. */}
      <Left1></Left1>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1</h1>
      <Left2></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2</h1>
      <Left3></Left3>
    </div>
  );
}

function Left3(props) {
  return (
    <div>
      <h1>Left3</h1>
    </div>
  );
}

 

펑션을 몇가지 더 추가합니다.

코드 페이스트 하다가 무한루프에 빠지지 않도록 조심합니다.

// 'Left1'이라는 이름의 함수형 컴포넌트입니다. 'props'를 인자로 받지만, 현재는 사용하고 있지 않습니다.
// 이 컴포넌트는 'App' 컴포넌트에 의해 렌더링됩니다.
function Left1(props) {
  return (
    // 'Left1' 컴포넌트의 UI를 정의합니다.
    <div>
      {/* "Left1"이라는 텍스트를 표시하는 제목입니다. */}
      <h1>Left1</h1>
      {/* 'Left2' 컴포넌트를 렌더링합니다. 이로써 컴포넌트의 중첩 구조가 이어집니다. */}
      <Left2></Left2>
    </div>
  );
}

test

벌건 박스를 체크 합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';

export default function App() {
  return (
    <div id="container">
      <h1>Root</h1>
      <Left1></Left1>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1</h1>
      <Left2></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2</h1>
      <Left3></Left3>
    </div>
  );
}

function Left3(props) {
  return (
    <div>
      <h1>Left3</h1>
    </div>
  );
}


데이터를 프롭스로 최상위 컴포넌트에서 최하위 컴포넌트까지 전달 합니다.


import { useState } from 'react';

 

유즈스테이트를 임포트 합니다.


  const [number, setNumber] = useState(1);

 

펑션앱 아래에 넘버 스테이트를 초기화 합니다.

  // 'useState' 훅을 사용하여 'number'라는 상태 변수와 이를 업데이트하는 'setNumber' 함수를 선언합니다.
  // 'number'의 초기값은 1로 설정됩니다.
  const [number, setNumber] = useState(1);

      <Left1 number={number}></Left1>

 

펑션앱 넘버를 데이터바인딩 합니다.

      {/* 'Left1' 컴포넌트를 렌더링합니다.
          이때 'number' 상태 값을 'props'로 'Left1' 컴포넌트에 전달합니다.
          이러한 데이터 전달 방식을 'prop drilling'이라고 합니다. */}
      <Left1 number={number}></Left1>

      <h1>Left1 : {props.number}</h1>
      <Left2 number={props.number}></Left2>

 

넘버를 받고 전달 합니다.

      {/* "Left1"이라는 텍스트와 함께 부모로부터 받은 'number' 값을 표시합니다.
          props.number는 App 컴포넌트의 number 상태 값(1)이 됩니다. */}
      <h1>Left1 : {props.number}</h1>
      {/* 'Left2' 컴포넌트를 렌더링합니다.
          다시 'props.number' 값을 'props'로 'Left2' 컴포넌트에 전달합니다. */}
      <Left2 number={props.number}></Left2>

      <h1>Left2 : {props.number}</h1>
      <Left3 number={props.number}></Left3>

 

넘버를 받고 전달 합니다.


      <h1>Left3 : {props.number}</h1>

 

넘버를 받습니다.


test

데이터가 잘 전달되는지 체크합니다.


Completion

 

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';

export default function App() {

  const [number, setNumber] = useState(1);

  return (
    <div id="container">
      <h1>Root</h1>
      <Left1 number={number}></Left1>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1 : {props.number}</h1>
      <Left2 number={props.number}></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2 : {props.number}</h1>
      <Left3 number={props.number}></Left3>
    </div>
  );
}

function Left3(props) {
  return (
    <div>
      <h1>Left3 : {props.number}</h1>
    </div>
  );
}


심심하니까 오른쪽에도 박스를 몇개 쌓아 봅니다.


function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
    </div>
  );
}

 

펑션레프트3 아래에 오른쪽 박스 하나 추가 합니다.


#grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

 

App.css

 

박스 스타일을 추가 합니다.


      <h1>Root : {number}</h1>

 

펑션앱 에이치태그에 넘버를 바인딩 합니다.


      <div id="grid">
        <Left1 number={number}></Left1>
        <Right1></Right1>
      </div>

 

펑션앱 내부에 레프트원을 그리드 아이디 디브로 래핑 합니다.

오른쪽 박스를 추가 합니다.


function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
    </div>
  );
}

 

오른쪽 박스를 몇개 더 만듭니다.


test

박스가 잘 쌓였는지 체크 합니다.


Completion

#container,
#container div {
  border: 5px solid red;
  margin: 10px;
}

#grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

App.css

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';

export default function App() {
  const [number, setNumber] = useState(1);

  return (
    <div id="container">
      <h1>Root : {number}</h1>
      <div id="grid">
        <Left1 number={number}></Left1>
        <Right1></Right1>
      </div>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1 : {props.number}</h1>
      <Left2 number={props.number}></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2 : {props.number}</h1>
      <Left3 number={props.number}></Left3>
    </div>
  );
}

function Left3(props) {
  return (
    <div>
      <h1>Left3 : {props.number}</h1>
    </div>
  );
}

function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
    </div>
  );
}

 



최하위 컴포넌트 버튼을 탭했을때 최상위 컴포넌트 데이터 변경 합니다.

Right3 버튼 탭하면 Root 데이터를 변경 합니다.


function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button"
        value="+"
        onClick={() => {
          props.onIncrease();
        }}
      ></input>
    </div>
  );
}

 

이벤트 코드를 역순으로 추가해 보겠습니다.

롸이트3에 더하기 유아이를 만들고 이벤트를 겁니다.

이 이벤트는 펑션앱 셋넘버 콜 합니다.

셋넘버 메서드는 아직 추가하지 않았습니다.

역순으로 올라가서 추가 합니다.

// Right3 컴포넌트는 제목과 함께 드디어 'number' 값을 증가시킬 버튼을 가지고 있어요!
// 이 버튼을 누르면 Right2, Right1을 거쳐 App 컴포넌트까지 전달된 onIncrease 기능이 실행됩니다.
function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button"
        value="+" // 버튼에 '+'라고 표시됩니다.
        onClick={() => {
          props.onIncrease(); // 버튼이 클릭되면 Right2에서 받은 onIncrease 기능을 실행합니다.
          // 이 onIncrease는 결국 App 컴포넌트의 setNumber(number + 1)을 호출하게 되죠!
        }}
      ></input>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3
        onIncrease={() => {
          props.onIncrease();
        }}
      ></Right3>
    </div>
  );
}

 

이벤트를 전달 합니다.

// Right2 컴포넌트도 제목을 보여주고, Right1에서 받은 onIncrease 기능을 Right3에게 다시 전달합니다.
function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      {/* Right1에서 받은 onIncrease 기능을 Right3에게 'onIncrease'라는 이름으로 전달합니다. */}
      <Right3
        onIncrease={() => {
          props.onIncrease(); // 받은 onIncrease를 그대로 실행합니다.
        }}
      ></Right3>
    </div>
  );
}

function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2
        onIncrease={() => {
          props.onIncrease();
        }}
      ></Right2>
    </div>
  );
}

 

이벤트를 전달 합니다.

// Right1 컴포넌트는 제목을 보여주고, App에서 받은 onIncrease 기능을 Right2에게 고스란히 전달해 줍니다.
function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      {/* App에서 받은 onIncrease 기능을 Right2에게 'onIncrease'라는 이름으로 전달합니다. */}
      <Right2
        onIncrease={() => {
          props.onIncrease(); // 받은 onIncrease를 그대로 실행합니다.
        }}
      ></Right2>
    </div>
  );
}

        <Right1
          onIncrease={() => {
            setNumber(number + 1);
          }}
        ></Right1>

 

드디어 메서드를 추가 합니다.

셋넘버를 1 증가 합니다.

        {/* Right1 컴포넌트에게는 'onIncrease'라는 특별한 기능을 전달해요!
            이 기능을 실행하면 `number` 값을 1 증가시키도록 약속하는 거죠. */}
        <Right1
          onIncrease={() => {
            setNumber(number + 1); // onIncrease가 호출되면 number를 1 증가시켜줍니다!
          }}
        ></Right1>

test

플러스버튼유아이를 3번 탭합니다.

우측 유아이가 왼쪽 박스에 어떤 영향를 주는지 체크합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';

export default function App() {
  const [number, setNumber] = useState(1);

  return (
    <div id="container">
      <h1>Root : {number}</h1>
      <div id="grid">
        <Left1 number={number}></Left1>
        <Right1
          onIncrease={() => {
            setNumber(number + 1);
          }}
        ></Right1>
      </div>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1 : {props.number}</h1>
      <Left2 number={props.number}></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2 : {props.number}</h1>
      <Left3 number={props.number}></Left3>
    </div>
  );
}

function Left3(props) {
  return (
    <div>
      <h1>Left3 : {props.number}</h1>
    </div>
  );
}

function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2
        onIncrease={() => {
          props.onIncrease();
        }}
      ></Right2>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3
        onIncrease={() => {
          props.onIncrease();
        }}
      ></Right3>
    </div>
  );
}

function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button"
        value="+"
        onClick={() => {
          props.onIncrease();
        }}
      ></input>
    </div>
  );
}

 



일부 코드 제거하고 리덕스 사용 준비를 합니다.


위 코드는 각각의 박스가 위아래로 아주 단단하게 체결되어 있습니다.

심심하니까 중간에 컴포넌트가 1억개가 있다라고 상상 합니다.

 

단전 깊숙한 곳에 봉인된 나의 흑염룡이 정수리까지 뚫고 올라옵니다.

하지만 리액트가 잘못한것은 없습니다.

리액트는 원래 그런 애니까요.

잘못한 애를 나무랄게 아니라 해결잘하는 애를 데려옵니다.

 

이런 빡치는 상황을 무선 연결로 처리할 수 있는 아이가 리덕스입니다.

 

리덕스를 사용하기 위해 레프트 롸이트에 단단하게 체결되어 있던 고리를 해제 합니다.




function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input type="button" value="+" onClick={() => {}}></input>
    </div>
  );
}

 

롸이트1/2 메소드를 삭제합니다.

롸이트3 온클릭메소드 내부 펑션을 삭제합니다.

// 'Right1' 컴포넌트는 제목을 표시하고 'Right2' 컴포넌트를 렌더링합니다.
function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

// 'Right2' 컴포넌트는 제목을 표시하고 'Right3' 컴포넌트를 렌더링합니다.
function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

// 'Right3' 컴포넌트는 Right 계층 구조의 가장 하단에 있으며, 제목과 버튼을 포함합니다.
function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button" // 버튼 타입의 input 요소입니다.
        value="+" // 버튼에 표시될 텍스트입니다.
        onClick={() => {}} // 버튼 클릭 시 실행될 함수입니다. 현재는 아무 동작도 하지 않습니다.
      ></input>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1 : </h1>
      <Left2></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2 : </h1>
      <Left3></Left3>
    </div>
  );
}

function Left3(props) {
  return (
    <div>
      <h1>Left3 : </h1>
    </div>
  );
}

 

레프트원투쓰리 h태그 데이터바인딩을 삭제합니다.

레프트원투 차일드태그 데이터바인딩을 삭제합니다.

// 'Left1' 컴포넌트는 단순히 제목을 표시하고 'Left2' 컴포넌트를 렌더링합니다.
// 이전 코드와 달리, 'App' 컴포넌트의 `number` 값을 전달받지 않습니다.
function Left1(props) {
  return (
    <div>
      <h1>Left1 : </h1>
      <Left2></Left2>
    </div>
  );
}

// 'Left2' 컴포넌트는 제목을 표시하고 'Left3' 컴포넌트를 렌더링합니다.
function Left2(props) {
  return (
    <div>
      <h1>Left2 : </h1>
      <Left3></Left3>
    </div>
  );
}

// 'Left3' 컴포넌트는 Left 계층 구조의 가장 하단에 있으며, 제목만 표시합니다.
function Left3(props) {
  return (
    <div>
      <h1>Left3 : </h1>
    </div>
  );
}

      <div id="grid">
        <Left1></Left1>
        <Right1></Right1>
      </div>

 

레프트원/롸이트원 태그 데이터바인딩/메소드를 삭제합니다.

      <div id="grid">
        {/* 'Left1' 컴포넌트를 렌더링합니다. 이 컴포넌트에는 현재 어떤 props도 전달되지 않습니다. */}
        <Left1></Left1>
        {/* 'Right1' 컴포넌트를 렌더링합니다. 이 컴포넌트에도 현재 어떤 props도 전달되지 않습니다. */}
        <Right1></Right1>
      </div>

test

연결이 모두 해제되었는지 체크 합니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';

export default function App() {
  const [number, setNumber] = useState(1);

  return (
    <div id="container">
      <h1>Root : {number}</h1>
      <div id="grid">
        <Left1></Left1>
        <Right1></Right1>
      </div>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1 : </h1>
      <Left2></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2 : </h1>
      <Left3></Left3>
    </div>
  );
}

function Left3(props) {
  return (
    <div>
      <h1>Left3 : </h1>
    </div>
  );
}

function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input type="button" value="+" onClick={() => {}}></input>
    </div>
  );
}


리덕스 인스톨 합니다.


https://lshjju.tistory.com/150

 

React.js - redux - 리액트 리덕스

Redux(리덕스)란 무엇인가요? Redux는 React(뿐만 아니라 다른 JavaScript 라이브러리/프레임워크)에서 **애플리케이션의 상태(State)를 효율적으로 관리하기 위한 예측 가능한 상태 컨테이너(Predictable Stat

lshjju.tistory.com



최상위 데이타와 최하위 데이터를 무선으로 연결합니다.


import { createStore } from "redux";

 

크리에이트스토어 임포트 합니다.

목주름은 레거시란 뜻이니 신경 안 써도 됩니다.

import { createStore } from 'redux'; // Redux 스토어를 생성하는 함수입니다.

const store = createStore();

 

스토어는 글로벌 스코프를 가져야 합니다.

그러므로 임포트 아래에 크리에이트스토어로 스토어 변수를 만듭니다.


function reducer(currentState, action) {
  if (currentState === undefined) {
    return {
      number: 1,
    };
  }
  const newState = { ...currentState };
  return newState;
}

 

스토어는 리듀서가 필숩니다.

리듀서 펑션을 만든 후에 스토어는 리듀서를 파라미터로 사용합니다.

그러므로 스토어 위에 펑션 리듀서를 추가 합니다.

 

리듀서는 현재스테이트/액션 두가지 파라미터를 사용합니다.

 

넘버 초기값을 여기서 지정합니다.

기존의 초기값은 나중에 삭제 합니다.

 

현재 스테이트를 복제하고 새로운 오브젝을 만듭니다.

스테이트는 결국 스토어로 스윽 넘어갑니다.

즉 앞으로 스토어는 리듀서에서 리턴하는 데이터를 사용합니다.

 

뷰를 보면 에러가 난것 같겠지만 괜찮습니다.

스토어 파라미터를 세팅하면 해결 됩니다.

// Reducer(리듀서) 함수는 애플리케이션의 상태를 변경하는 방법을 정의합니다.
// `currentState`와 `action`을 인자로 받아 새로운 상태를 반환합니다.
function reducer(currentState, action) {
  // 스토어가 처음 생성될 때 currentState가 undefined이므로 초기 상태를 설정합니다.
  if (currentState === undefined) {
    return {
      number: 1, // 'number'라는 상태 변수의 초기값을 1로 설정합니다.
    };
  }
  // 현재 상태를 복사하여 새로운 상태 객체를 만듭니다. (불변성을 유지하기 위함)
  const newState = { ...currentState };
  // 현재는 어떤 액션이 오더라도 상태를 변경하지 않고 현재 상태를 그대로 반환합니다.
  return newState;
}

const store = createStore(reducer);

 

스토어의 파라미터로 리듀서를 사용합니다.

그리고 스토어는 리듀서가 리턴한 스테이트를 업데이트 합니다.

// `reducer` 함수를 사용하여 Redux 스토어를 생성합니다.
// 이 스토어가 애플리케이션의 모든 상태를 중앙에서 관리하게 됩니다.
const store = createStore(reducer);

 

  const [number, setNumber] = useState(1); // 삭제

 

넘버는 이제부터 스토어에서 관리 합니다.

펑션앱 넘버 스테이트는 이제 필요 없으니 삭제 합니다.


      <h1>Root</h1>

 

h1태그 데이터바인딩도 당연히 삭제 합니다.


import { Provider, useSelector, useDispatch, connect } from "react-redux";

 

필요한 기능을 임포트 합니다.

프로바이더는 대문자로 시작 합니다.

connect 는 이 프로젝트에서는 사용하지 않습니다.

import { Provider, useSelector, useDispatch, connect } from 'react-redux'; 
// React 애플리케이션에 Redux를 연결하기 위한 도구들입니다.
// - Provider: Redux 스토어를 React 컴포넌트 트리에 제공합니다.
// - useSelector: Redux 스토어의 상태를 컴포넌트에서 선택하여 가져올 수 있게 합니다.
// - useDispatch: Redux 스토어에 액션을 보내기 위한 함수를 가져올 수 있게 합니다. (현재 코드에서는 사용되지 않습니다.)
// - connect: 클래스형 컴포넌트에서 Redux 스토어에 연결하는 고차 컴포넌트입니다. (현재 코드에서는 사용되지 않습니다.)

      <div id="grid">
        <Provider store={store}>
          <Left1></Left1>
          <Right1></Right1>
        </Provider>
      </div>

 

펑션앱 내부에 프로바이더로 특정 할 최상위 태그를 래핑해서 스토어에 밸류를 배정 합니다.

이제 프로바이더 내부 컴포넌트는 스토어를 사용할 권리를 얻었습니다.

      <div id="grid">
        {/* <Provider> 컴포넌트를 사용하여 생성된 'store'를 하위 컴포넌트들에게 제공합니다.
            <Provider> 내부에 있는 모든 컴포넌트는 Redux 스토어의 상태에 접근할 수 있습니다. */}
        <Provider store={store}>
          {/* 'Left1' 컴포넌트와 'Right1' 컴포넌트를 렌더링합니다. */}
          <Left1></Left1>
          <Right1></Right1>
        </Provider>
      </div>

  const number = useSelector(f);

 

펑션레프트3 리턴 위에 넘버를 무선으로 연결 하기 위해 넘버를 유즈셀렉터로 초기화 합니다.

유즈셀렉터는 펑션을 파라미터로 사용합니다.

f는 임시로 사용할 네임입니다.


  function f(state) {
    return state.number;
  }

 

펑션레프트3 넘버변수 아래에 에프펑션을 추가합니다.

스테이트를 파라미터로 사용합니다.

스테이트중 넘버변수를 사용하겠습니다. 


  const number = useSelector((state) => state.number);

 

펑션레프트3 코드체지방을 줄입니다.

펑션을 유즈셀렉터 파라미터 안에 장착 합니다.

  // `useSelector` 훅을 사용하여 Redux 스토어의 상태 중 'number' 값을 가져옵니다.
  // 이제 `number`는 App 컴포넌트에서 props로 전달받는 대신 Redux 스토어에서 직접 가져오게 됩니다.
  const number = useSelector((state) => state.number);

      <h1>Left3 : {number}</h1>

 

펑션레프트3 데이터 바인딩 합니다.

축하 합니다.

이제 스토어 넘버와 h태그의 넘버가 무선으로 연결되었습니다.

      <h1>Left3 : {number}</h1> {/* Redux 스토어에서 가져온 'number' 값을 표시합니다. */}

test

이제 루트와 레프트3 데이터가 무선으로 잘 연결 되는지 체크 합니다.

스토어 현재밸류인 1이 보인다면 성공입니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch, connect } from 'react-redux';

function reducer(currentState, action) {
  if (currentState === undefined) {
    return {
      number: 1,
    };
  }
  const newState = { ...currentState };
  return newState;
}

const store = createStore(reducer);

export default function App() {
  return (
    <div id="container">
      <h1>Root</h1>
      <div id="grid">
        <Provider store={store}>
          <Left1></Left1>
          <Right1></Right1>
        </Provider>
      </div>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1 : </h1>
      <Left2></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2 : </h1>
      <Left3></Left3>
    </div>
  );
}

function Left3(props) {
  const number = useSelector((state) => state.number);
  return (
    <div>
      <h1>Left3 : {number}</h1>
    </div>
  );
}

function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input type="button" value="+" onClick={() => {}}></input>
    </div>
  );
}


롸잇3 박스 유아이 플러스를 탭하면 레프트3 데이터가 변경 됩니다.


  const dispatch = useDispatch();

 

펑션롸이트3 이벤트 액션을 리듀서에 전달해야 합니다.

딴일은 안하고 이 일만 열심히 하는 아이가 디스패치입니다.

펑션롸이트3 리턴위에 디스패치를 유즈디스패치로 초기화 합니다.

  // `useDispatch` 훅을 사용하여 Redux 스토어에 액션을 보낼 수 있는 `dispatch` 함수를 가져옵니다.
  const dispatch = useDispatch();

      <input
        type="button"
        value="+"
        onClick={() => {
          dispatch({ type: "PLUS" });
        }}
      ></input>

 

펑션롸이트3 인풋 디스패치 타입을 디파인 합니다.

액션은 오브젝이니까 기분 좋게 오브젝스타일로 디파인하면 좋겠습니다.

이 플러스를 탭하면 리듀서 콜 합니다.

      <input
        type="button" // 버튼 타입의 input 요소입니다.
        value="+" // 버튼에 표시될 텍스트입니다.
        onClick={() => {
          // 버튼 클릭 시, `dispatch` 함수를 호출하여 { type: 'PLUS' } 액션을 Redux 스토어에 보냅니다.
          // 이 액션은 `reducer` 함수에 의해 처리되어 `number` 상태를 증가시킵니다.
          dispatch({ type: 'PLUS' });
        }}
      ></input>

  if (action.type === "PLUS") {
    newState.number++;
  }

 

펑션리듀서 리턴 위에 액션타입에 대응하는 이프문을 작성합니다.

결과가 참이라면 증감연산자를 사용해서 데이터를 1증가시킵니다.

뉴스테이트가 리턴 됩니다.

  // `action.type`이 'PLUS'일 경우, `number` 상태를 1 증가시킵니다.
  if (action.type === 'PLUS') {
    newState.number++;
  }

 test

플러스 유아이를 탭합니다.

탭할 때 레프트3 데이터가 증가하는지 체크 합니다.

즉, 플러스를 탭하면 스테이트가 변경 됩니다.

스테이트가 변경되면 레프트3 데이터가 변경됩니다.


Completion

import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch, connect } from 'react-redux';

function reducer(currentState, action) {
  if (currentState === undefined) {
    return {
      number: 1,
    };
  }
  const newState = { ...currentState };
  if (action.type === 'PLUS') {
    newState.number++;
  }
  return newState;
}

const store = createStore(reducer);

export default function App() {
  return (
    <div id="container">
      <h1>Root</h1>
      <div id="grid">
        <Provider store={store}>
          <Left1></Left1>
          <Right1></Right1>
        </Provider>
      </div>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1 : </h1>
      <Left2></Left2>
    </div>
  );
}

function Left2(props) {
  return (
    <div>
      <h1>Left2 : </h1>
      <Left3></Left3>
    </div>
  );
}

function Left3(props) {
  const number = useSelector((state) => state.number);
  return (
    <div>
      <h1>Left3 : {number}</h1>
    </div>
  );
}

function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

function Right3(props) {
  const dispatch = useDispatch();

  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button"
        value="+"
        onClick={() => {
          dispatch({ type: 'PLUS' });
        }}
      ></input>
    </div>
  );
}

Comment ver

import { useState } from 'react'; // React에서 상태를 관리하는 'useState' 훅을 가져옵니다. (현재 이 코드에서는 직접 사용되지 않습니다.)
import reactLogo from './assets/react.svg'; // React 로고 이미지 파일을 가져옵니다. (코드 내에서 사용되지 않습니다.)
import viteLogo from '/vite.svg'; // Vite 로고 이미지 파일을 가져옵니다. (코드 내에서 사용되지 않습니다.)
import './App.css'; // 'App' 컴포넌트의 스타일을 정의하는 CSS 파일을 가져옵니다.

// Redux(리덕스) 관련 모듈을 가져옵니다.
import { createStore } from 'redux'; // Redux 스토어를 생성하는 함수입니다.
import { Provider, useSelector, useDispatch, connect } from 'react-redux';
// React 애플리케이션과 Redux 스토어를 연결하는 데 필요한 도구들을 가져옵니다.
// - Provider: React 컴포넌트 트리에 Redux 스토어를 제공합니다.
// - useSelector: Redux 스토어의 상태를 컴포넌트 내에서 선택적으로 조회할 수 있도록 합니다.
// - useDispatch: Redux 스토어에 액션(상태 변경 요청)을 보낼 수 있는 함수를 가져옵니다.
// - connect: (이 코드에서는 사용되지 않음) 클래스형 컴포넌트에서 Redux 스토어와 연결할 때 사용되는 고차 컴포넌트입니다.

// `reducer` 함수는 애플리케이션의 상태를 변경하는 방식을 정의합니다.
// `currentState`(현재 상태)와 `action`(발생한 이벤트)을 인자로 받아 새로운 상태를 반환합니다.
function reducer(currentState, action) {
  // 스토어가 최초 생성될 때 `currentState`가 `undefined`이므로, 초기 상태를 설정합니다.
  if (currentState === undefined) {
    return {
      number: 1, // `number` 상태 변수의 초기값을 1로 설정합니다.
    };
  }
  // 현재 상태를 복사하여 새로운 상태 객체를 생성합니다. 이는 Redux의 불변성(immutability) 원칙을 따르기 위함입니다.
  const newState = { ...currentState };

  // `action.type`이 'PLUS'일 경우, `number` 상태를 1 증가시킵니다.
  if (action.type === 'PLUS') {
    newState.number++;
  }
  return newState; // 변경된 새로운 상태를 반환합니다.
}

// `reducer` 함수를 사용하여 Redux 스토어를 생성합니다.
// 이 스토어가 애플리케이션의 모든 전역 상태를 관리하게 됩니다.
const store = createStore(reducer);

// 'App' 컴포넌트는 애플리케이션의 최상위 컴포넌트입니다.
// 이 컴포넌트에서는 `useState`를 직접 사용하여 상태를 관리하는 대신, Redux 스토어를 사용합니다.
export default function App() {
  return (
    <div id="container">
      <h1>Root</h1> {/* 'Root' 제목만 표시되며, `number` 값은 하위 컴포넌트에서 Redux를 통해 표시됩니다. */}
      <div id="grid">
        {/* <Provider> 컴포넌트를 사용하여 생성된 'store'를 하위 컴포넌트 트리에 제공합니다.
            <Provider> 내부에 있는 모든 컴포넌트는 이 스토어의 상태에 접근할 수 있게 됩니다. */}
        <Provider store={store}>
          <Left1></Left1> {/* 'Left1' 컴포넌트를 렌더링합니다. */}
          <Right1></Right1> {/* 'Right1' 컴포넌트를 렌더링합니다. */}
        </Provider>
      </div>
    </div>
  );
}

// 'Left1' 컴포넌트는 단순히 제목을 표시하고 'Left2' 컴포넌트를 렌더링합니다.
// Redux 스토어의 상태를 직접 사용하거나 props로 전달받지는 않습니다.
function Left1(props) {
  return (
    <div>
      <h1>Left1 : </h1>
      <Left2></Left2>
    </div>
  );
}

// 'Left2' 컴포넌트는 제목을 표시하고 'Left3' 컴포넌트를 렌더링합니다.
function Left2(props) {
  return (
    <div>
      <h1>Left2 : </h1>
      <Left3></Left3>
    </div>
  );
}

// 'Left3' 컴포넌트는 Redux 스토어의 상태를 직접 조회하여 사용합니다.
function Left3(props) {
  // `useSelector` 훅을 사용하여 Redux 스토어의 전체 상태(state)에서 `number` 값을 가져옵니다.
  const number = useSelector((state) => state.number);
  return (
    <div>
      <h1>Left3 : {number}</h1> {/* Redux 스토어에서 가져온 `number` 값을 표시합니다. */}
    </div>
  );
}

// 'Right1' 컴포넌트는 제목을 표시하고 'Right2' 컴포넌트를 렌더링합니다.
function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2></Right2>
    </div>
  );
}

// 'Right2' 컴포넌트는 제목을 표시하고 'Right3' 컴포넌트를 렌더링합니다.
function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3></Right3>
    </div>
  );
}

// 'Right3' 컴포넌트는 제목과 버튼을 포함하며, 이 버튼을 통해 Redux 스토어에 액션을 보냅니다.
function Right3(props) {
  // `useDispatch` 훅을 사용하여 Redux 스토어에 액션을 보낼 수 있는 `dispatch` 함수를 가져옵니다.
  const dispatch = useDispatch();

  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button" // 버튼 타입의 input 요소입니다.
        value="+" // 버튼에 표시될 텍스트입니다.
        onClick={() => {
          // 버튼 클릭 시, `dispatch` 함수를 호출하여 { type: 'PLUS' } 액션을 Redux 스토어에 보냅니다.
          // 이 액션은 `reducer` 함수에 의해 처리되어 `number` 상태를 증가시킵니다.
          dispatch({ type: 'PLUS' });
        }}
      ></input>
    </div>
  );
}


출처

https://wikibook.co.kr/react-rev-ebook/

 

생활코딩! React 리액트 프로그래밍 (개정판) (ebook): 처음 프로그래밍을 시작하는 입문자의 눈높이

세상에서 리액트를 가장 쉽게 설명한 입문서! 생활코딩은 일반인에게 프로그래밍을 알려주는 것을 목적으로 하는 비영리 교육 활동입니다. 이 책은 생활코딩에서 제공하는 수업 가운데 리액트

wikibook.co.kr