지난 시간에 배웠던 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 prop과 하위 컴포넌트에서 상위 컴포넌트로 데이터를 전달하는 event를 사용하는 방법을 복습!
import React, { useState } from 'react'
import './App.css';
function App() {
const [number,SetNumber] = useState(0)
function Add(){
SetNumber(number+1)
}
function Reset(){
SetNumber(0)
}
function Right1(){
return(
<div>
<h2>Right1 : {number}</h2>
<Right2 number={number}></Right2>
</div>
)
}
function Right2({number}){
return(
<div>
<h2>Right2 : {number}</h2>
<Right3 number={number}></Right3>
</div>
)
}
function Right3({number}){
return(
<div>
<h2>Right3 : {number}</h2>
</div>
)
}
function Left1({onChangeNumber}){
return(
<div>
<h2>Left1 : {number}</h2>
<Left2 number={number}
onChangeNumber={()=>onChangeNumber()}
></Left2>
</div>
)
}
function Left2({number, onChangeNumber}){
return(
<div>
<h2>Left2 : {number}</h2>
<Left3 number={number}
onChangeNumber={()=>onChangeNumber()}
></Left3>
</div>
)
}
function Left3({number, onChangeNumber}){
return(
<div>
<h3>Left3 : {number}</h3>
<p>
<button
onClick={
()=>onChangeNumber()
}>Add</button>
</p>
</div>
)
}
return (
<div className="root">
<h1>Root: {number}</h1>
<button
onClick={Reset}>
Reset
</button>
<div className="container">
<Left1
number={number}
onChangeNumber={()=>SetNumber(number+1)}
>
</Left1>
<Right1 number={number}>
</Right1>
</div>
</div>
)
}
export default App
(결과)

처음엔 Add 버튼이 루트 컴포넌트에 있었는데 제일 하위 컴포넌트로 옮겨서 데이터가 전달되는 방식을 복습해보았다.
데이터가 잘 전달되고 있는지 확인하기
전달되고 있는 number을 삭제해서 가장 하위 컴포넌트의 상태만 남기고 데이터가 전달되는지 확인하는 방법을 사용했다.
import React, { useState } from 'react'
import './App.css';
function App() {
const [number,SetNumber] = useState(0)
function Add(){
SetNumber(number+1)
}
function Reset(){
SetNumber(0)
}
function Right1(){
return(
<div>
<h2>Right1</h2>
<Right2 number={number}></Right2>
</div>
)
}
function Right2(){
return(
<div>
<h2>Right2</h2>
<Right3 number={number}></Right3>
</div>
)
}
function Right3({number}){
return(
<div>
<h2>Right3 : {number}</h2>
</div>
)
}
function Left1({onChangeNumber}){
return(
<div>
<h2>Left1</h2>
<Left2
onChangeNumber={()=>onChangeNumber()}
></Left2>
</div>
)
}
function Left2({ onChangeNumber}){
return(
<div>
<h2>Left2</h2>
<Left3
onChangeNumber={()=>onChangeNumber()}
></Left3>
</div>
)
}
function Left3({onChangeNumber}){
return(
<div>
<h3>Left3</h3>
<p>
<button
onClick={
()=>onChangeNumber()
}>Add</button>
</p>
</div>
)
}
return (
<div className="root">
<h1>Root</h1>
<button
onClick={Reset}>
Reset
</button>
<div className="container">
<Left1
number={number}
onChangeNumber={()=>SetNumber(number+1)}
>
</Left1>
<Right1 number={number}>
</Right1>
</div>
</div>
)
}
export default App
(결과)

ADD 버튼을 누르면 Right3의 숫자가 늘어나고, Reset을 누르면 0으로 초기화 된다.
전역 컴포넌트를 관리하는 상태 관리 도구이다. 리액트용 도구가 아닌 자바스크립트를 사용하는 환경이라면 모두 사용 가능하다.
리액트 환경에서 사용하는데 최적화된 리덕스

redux와 react용 redux를 함께 설치했다.
여러 가지의 모듈을 한 번에 설치할 땐 한 줄로 나열해서 설치하는 방법도 있다!

데이터 저장소: store
store 안의 내용을 바꾸는 내용: reducers 안에 저장되어있음
동작을 불러오는 action
useReducer와 사용법이 비슷하다!
그러나 useReducer에서는 전역변수를 관리하는 것은 아니다.
userReducer와 context api를 함께 사용하면 redux와 비슷한 효과를 낼 수 있다.
UI는 보여지는 화면
action은 reducer안에 들어있는 두 번째 인자. ditpatch에서 부르면 실행된다.(포스기에서 보이는 메뉴. 메뉴이름)
dispatch reducer가 무엇을 할지 요청하는 함수/저장소의 내용을 바꾸는 함수(주문하기)
reducers는 일을 수행하는 곳(바리스타가 음료 제조!)
store는 수행한 내용이 반영되는 곳(음료 받는곳)
배열을 복제해서 사용해야 한다!!
spread 연산자 사용! 배열의 이름 앞에 ...을 붙이면 됨
const newState = {currentState}
=> Array.map()에서 사용되는 방법처럼 배열을 복제해서 사용하면 기존의 데이터는 유지하되 변경 되는 데이터만 수정할 수 있다.
저장소를 사용할 영역을 provider로 묶어줘야 한다
전체를 사용할 것이 아니라, 일부의 데이터만 수정할 것이기 때문에 provider를 이용한다.
데이터를 불러오기 위해서는 useSelector을 사용한다
vuex에서의 getters 역할을 해준다.
(정리)
//저장소 생성
function App(){
function reducer(currentState,action){
const newState = {...currentState}
return newState
}
const store = createStore(reducer)
return(
<Provider store={store}>
</Provider>
)
//데이터 호출
function Right3(){
function get_state(state){
return state
}
const number = userSelector(get_state)
return(
<div>
<h3>Right 3: {number}</h3>
</div>
)
}
+) sugar 문법이란?
간단하게 적을 수 있는 문법 ex) 삼항연산자
(redux를 이용해 데이터를 전달하는 예시)
import React, { useState } from 'react';
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
import './App.css';
function App() {
const [number, setNumber] = useState(0);
// Reducer 함수 생성
function reducer(currentState, action) {
if(currentState === undefined){
return{
number:1,
payload:2
}
}
const newState = { ...currentState };
if (action.type === "Add") {
newState.number++;
}
return newState;
}
// 저장소 생성
const store = createStore(reducer);
function Add() {
setNumber(number + 1);
}
function Reset() {
setNumber(0);
}
function Right1() {
return (
<div>
<h2>Right1</h2>
<Right2 />
</div>
);
}
function Right2() {
return (
<div>
<h2>Right2</h2>
<Right3 />
</div>
);
}
function Right3() {
const number = useSelector(state => state.number);
return (
<div>
<h2>Right3 : {number}</h2>
</div>
);
}
function Left1() {
return (
<div>
<h2>Left1</h2>
<Left2 />
</div>
);
}
function Left2() {
return (
<div>
<h2>Left2</h2>
<Left3 />
</div>
);
}
function Left3() {
const dispatch = useDispatch();
return (
<div>
<h2>Left3</h2>
<p>
<button onClick={() => dispatch({ type: "Add" })}>Add</button>
</p>
</div>
);
}
return (
<Provider store={store}>
<div className="root">
<h1>Root</h1>
<button onClick={Reset}>Reset</button>
<div className="container">
<Left1 />
<Right1 />
</div>
</div>
</Provider>
);
}
export default App;
(정리)
import React from 'react'
import {Provider,useDispatch,useSelector} from 'react-redux'
import {createStore} from 'redux'
function reducer(state, action){
if(action.type==="ADD"){
return {...state, value: state.value+action.step}
}
return state
}
const initialValue = {value:0, number:1, menu:"coffee"}
const store = createStore(reducer, initialValue)
function Counter(){
const count = useSelector( state => state.value)
const dispatch = useDispatch()
return(
<div>
<p>Count : {count}</p>
<button
onClick={
()=>dispatch({type:"ADD", step:2})
}
>ADD</button>
</div>
)
}
function App() {
return (
<Provider store={store}>
<div>
<Counter></Counter>
</div>
</Provider>
)
}
export default App
하나의 저장소 안에 작은 저장소 여러개를 만들 수 있는 도구
npm install @reduxjs/toolkit

설치 후 package.json 확인!
Import 해서 configureStore과 createSlice를 불러온다.
import {configureStore, createSlice} from '@reduxjs/toolkit'
작은 저장소를 생성한다.
괄호 안에는 Options 가 필요하다. 필요한 내용을 직접 담으면 됨
ex) const counterSlice = createSlice({
name:,
initialState:,
reducers:{
ADD: ()=>{},
.
.
.
}
})
const 변수명1 = createSlice();
const 변수명2 = createSlice();
const store = configureStore({
reducer:{
변수 이름1: 변수명1,
변수 이름2: 변수명2
}
})
toolkit에서는 action을 위한 조건문을 사용하지 않고 reducers를 객체형식으로 만든 뒤 key에 함수를 담는다.
(정리)
import React from 'react'
import {Provider,useDispatch,useSelector} from 'react-redux'
import {configureStore, createSlice} from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: "counterSlice",
initialState: { value:0, number:1 },
reducers:{
ADD:(state, action)=>{state.value = state.value + action.step},
SUB: (state, action)=>{state.value = state.value - action.step}
}
})
const timerSlice = createSlice(
{
name: "timerSlice",
initialState: { aa:0, bb:1 },
reducers:{
INCREMENT:()=>{},
DECREMENT: ()=>{}
}
}
)
const store = configureStore({
reducer:{
counter: counterSlice.reducer,
timer: timerSlice.reducer
}
})
function Counter(){
const count = useSelector( state => {
//console.log(state)
return state.counter.value
})
const dispatch = useDispatch()
return(
<div>
<p>Count : {count}</p>
<button
onClick={
()=>dispatch({type:"counterSlice/ADD", step:2})
}
>ADD</button>
<button
onClick={
()=>dispatch({type:"counterSlice/SUB", step:2})
}>SUB</button>
</div>
)
}
function App() {
return (
<Provider store={store}>
<div>
<Counter></Counter>
</div>
</Provider>
)
}
export default App
dispatch({type:"counterSlice/ADD"}) 로 전달했던 부분을 action으로 전달하는 방법, 방법을 다르게 전달하는 것임
(예시)
onClick = {
() = > dispatch(
counterSlice.actions.ADD(2)
)
}
(정리)
import React from 'react'
import {Provider,useDispatch,useSelector} from 'react-redux'
import {configureStore, createSlice} from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: "counterSlice",
initialState: { value:0, number:1 },
reducers:{
//ADD:(state, action)=>{state.value = state.value + action.step},
ADD: (state, action)=>{
console.log(action)
state.value = state.value + action.payload
},
SUB: (state, action)=>{state.value = state.value - action.step}
}
})
const timerSlice = createSlice(
{
name: "timerSlice",
initialState: { aa:0, bb:1 },
reducers:{
INCREMENT:()=>{},
DECREMENT: ()=>{}
}
}
)
const store = configureStore({
reducer:{
counter: counterSlice.reducer,
timer: timerSlice.reducer
}
})
function Counter(){
const count = useSelector( state => {
//console.log(state)
return state.counter.value
})
const dispatch = useDispatch()
return(
<div>
<p>Count : {count}</p>
<button
onClick={
// ()=>dispatch({type:"counterSlice/ADD", step:2})
()=>dispatch(counterSlice.actions.ADD(2))
}
>ADD</button>
<button
onClick={
()=>dispatch({type:"counterSlice/SUB", step:2})
}>SUB</button>
</div>
)
}
function App() {
return (
<Provider store={store}>
<div>
<Counter></Counter>
</div>
</Provider>
)
}
export default App
//app.js
import React from 'react'
import {Provider,useDispatch,useSelector} from 'react-redux'
import counterSlice from './components/CounterSlice'
import store from './components/ConfigureStore'
function Counter(){
const count = useSelector( state => {
//console.log(state)
return state.counter.value
})
const dispatch = useDispatch()
return(
<div>
<p>Count : {count}</p>
<button
onClick={
// ()=>dispatch({type:"counterSlice/ADD", step:2})
()=>dispatch(counterSlice.actions.ADD(2))
}
>ADD</button>
<button
onClick={
()=>dispatch({type:"counterSlice/SUB", step:2})
}>SUB</button>
</div>
)
}
function App() {
return (
<Provider store={store}>
<div>
<Counter></Counter>
</div>
</Provider>
)
}
export default App
//CounterSlice.js
import { createSlice } from "@reduxjs/toolkit"
const counterSlice = createSlice({
name: "counterSlice",
initialState: { value:0, number:1 },
reducers:{
//ADD:(state, action)=>{state.value = state.value + action.step},
ADD: (state, action)=>{
console.log(action)
state.value = state.value + action.payload
},
SUB: (state, action)=>{state.value = state.value - action.step}
}
})
export default counterSlice
//TimerSlice.js
import { createSlice } from "@reduxjs/toolkit";
const timerSlice = createSlice(
{
name: "timerSlice",
initialState: { aa:0, bb:1 },
reducers:{
INCREMENT:()=>{},
DECREMENT: ()=>{}
}
}
)
export default timerSlice
//ConfigureStore.js
import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "./CounterSlice";
import timerSlice from "./TimerSlice";
const store = configureStore({
reducer:{
counter: counterSlice.reducer,
timer: timerSlice.reducer
}
})
export default store
상수를 내보내기 할 땐 {} 중괄호에 묶어서 내보낸다.
CounterSlice.js에서 필요한 데이터는 ADD
필요한 데이터만 내보내기 하고싶으면
//CounterSlice.js
export const {ADD} = counterSlice.actions
//app.js
import { ADD } from './components/CounterSlice'
<button
onClick={
// ()=>dispatch({type:"counterSlice/ADD", step:2})
//()=>dispatch(counterSlice.actions.ADD(2))
()=>dispatch(ADD(2))
}>
ADD</button>