useReducer 왜 필요한가?

state의 갯수가 너무 많아지면 state와 setState함수들의 관리가 매우 어려워 지고, props로 넘겨줄 때 매우 많은 것을 넘겨줘야하는 경우가 왕창 생긴다.

그럴 때 state자체의 갯수를 줄이기 위해 필요하다고 함. (아직 감이 안오긴한다.)

 

TicTacToe.jsx의 구조는 아래와 같다.

TicTacToe > Table > Tr > Td

state들은 모두 TicTacToe.jsx에 있고 우리가 실제로 누르는 것은 Td컴포넌트 인데.. 이렇게 되면 state들과 setState함수들을 Table, Tr을 거쳐 Td까지 전달해주어야 한다.

 

이런 번거로운 경우를 해결하기 위해 ContextAPI를 많이 사용하지만 ContextAPI는 다음시간에 다뤄볼것이고, 이번에는 state 자체의 개수를 줄이는 useReducer를 다뤄본다고 함.

 

현재 winner, turn, tableData라는 state가 있는데 useReducer를 사용하면 딱 하나의 state와 하나의 setState함수로 통일할 수 있다고 한다.

    //아래의 세가지 스테이트를 한개로 통일 가능.
    const [winner, setWinner] = useState('');
    const [turn, setTurn] = useState('O');
    const [tableData, setTableData] = useState([['','',''],['','',''],['','','']]);
    
    //아래와 같이 통일이 가능하다.
    const [state, dispatch] = useReducer(reducer, initialState);

복잡한 state들이 한개로 통일되니까 가독성이 훨씬 좋아지는 것 같다.

 

1. 이를 위해선 당연히 useReducer의 import가 필요하다.

import {useReducer} from 'react';

 

2. initialState(초기 상태값들)

//state 초기값
const initialState = {
    winner : '',
    turn: 'O',
    tableData:[['','',''],['','',''],['','','']],
}

 

3. reducer (배열의 리듀서 함수처럼 어떤것을 줄이기 위한 함수!)

//리듀서 == 함수
const reducer = (state, action) => {

}

 

※ ContextAPI + useReducer = 소규모 앱에서 리덕스를 대체할 순 있지만 규모가 큰 앱이면 결국엔 리덕스가 필요하다고함.

 

 

현재까지의 코드

TicTacToe.jsx

import React, {useReducer, useState} from "react";
import Table from "./Table";

//state 초기값
const initialState = {
    winner : '',
    turn: 'O',
    tableData:[['','',''],['','',''],['','','']],
}
//리듀서 == 함수
const reducer = (state, action) => {

}

const TicTacToe = () => {
    const [winner, setWinner] = useState('');
    const [turn, setTurn] = useState('O');
    const [tableData, setTableData] = useState([['','',''],['','',''],['','','']]);

    const [state, dispatch] = useReducer(reducer, initialState);

    return(
        <>
            <Table />
            {
                winner && <div>{winner}님의 승리</div>
            }
        </>
    );
}

export default TicTacToe;

 

Table.jsx

import React from 'react';
import Tr from "./Tr";

const Table = () => {
    return (
        <Tr>
            
        </Tr>
    );
};

export default Table;

 

Tr.jsx

import React from 'react';
import Td from "./Td";

const Tr = () => {
    return (
        <Td>

        </Td>
    );
};

export default Tr;

 

Td.jsx

import React from 'react';

const Td = () => {
    return (
        <td>

        </td>
    );
};

export default Td;
  • Hooks를 조건문 안에 절대 넣으면 안되고, 함수나 반복문 안에도 웬만하면 넣지 말자.
    • 아래 코드와 같이 hooks는 조건문 안에 넣으면 안된다. 반복문의 참,거짓여부에 따라 실행되는 순서가 달라지면 안되기 때문.
useState([]);
useState(1);
if(~){
	useState(false);
}
useMemo
  • 다른 hooks안에 hooks를 넣지 말자. 
    • useEffect, useCallback, useMemo가 실행될 수도있고 안될수도 있는 상황이 있을 수도 있기 때문.
useEffect(()=>{
	useState(~);
},[]);

useCallback(()=>{
	useState(~);
},[]);

useMemo(()=>{
	useState(~);
},[]);

 

지금까지의 내용 정리

  • useMemo는 값을 저장한다. (함수의 반환값), 의존배열 요소들이 바뀌기 전까지
  • useCallback은 함수 자체를 기억한다. 의존배열 요소들이 바뀌기 전까지
  • useEffect는 전달받은 콜백함수를 실행한다. 의존배열 요소들이 바뀔 때.
  • 두번째 배열들의 인자들이 바뀌면 앞에것들이 다시 실행된다!

useEffect심화

  • componentDidMount에서는 아무것도 안하고, componentDidUpdate에서만 ajax 요청을 하고싶다면? 아래의 패턴을 기억하자.
const mounted = useRef(false);
useEffect(()=>{
	if(!mounted.current){
   	 	mounted.current = true; //componentDidMount를 막을 순 없으니 이렇게 아무것도 안하도록 해두면 된다.
    } else {
    	//ajax 요청
    }
},[바뀌는값])
  • componentDidUpdate만 하고 싶다
useEffect(()=>{
	//ajax 요청하면됨.
},[])
componentDidUpdate(prevProps,prevState){
	if(prevState.winNumbers !== this.state.winNumbers){
    	console.log('로또 숫자를 생성');
    }
   	if(~){
    }
}

useState(()=>{
	console.log('로또 숫자를 생성');
},[winNumbers])

componentDidUpdate는 하나의 메서드에서 모든 state를 나열해서 사용한다. (가로)

hooks에서 useEffect는 따로따로 사용.. state들이 바뀌었을 때 각각 실행되어야하는 코드가 다르면 useEffect를 여러 개 만들면 된다. (세로)

componentDidUpdate는 한방에 해당 메서드에서 처리

hooks에서는 useEffect별로 따로따로.

docker run 명령어와 자주 쓰이는 옵션들 정리

우분투 컨테이너 다운로드 및 실행

docker run -d --name myubuntu ubuntu

run을 한 다음 docker ps 를 했을 때 실행 중인 우분투 컨테이너가 보일 줄 알았지만 해당 컨테이너는 실행과 동시에 종료되어 docker ps -a를 해야만 볼 수 있다.

 

우분투 컨테이너가 실행과 동시에 종료되는 이유

https://ilikecoding.tistory.com/78

 

도커 입문 6강. 컨테이너 실행하기

컨테이너 특징(생명주기 관련)컨테이너 내부에 프로그램이 일회성 이라면 컨테이너를 띄우자마자 종료되어버린다.즉, 컨테이너에서 실행될 프로그램이 데몬처럼 백그라운드에 떠있으며 무언

ilikecoding.tistory.com

6강에서도 설명하셨는데.. 컨테이너 내부 프로그램이 일회성이라면 컨테이너를 띄우자마자 종료되어버린다고함.

 

 

우분투 이미지는 내부에 실행될 프로그램이 없기 때문에 바로 종료.. httpd는 내부에 아파치 프로그램이 무한루프로 돌고 있기 때문에 종료되지 않는다.

 

[우분투 이미지 실행]

우분투 ▶ RUN ▶ 죽음(내부에 실행할 프로그램이 없다.)

[httpd 이미지 실행]

httpd ▶ RUN ▶ 내부에 무한루프로 Listener가 돌고있어서 종료되지 않는다.
httpd image = ubuntu + apache 인 것.

 

우분투 이미지 종료를 막는 방법

docker run -dit --name myubuntu ubuntu

바로 -it 옵션을 사용하는 것이다.

-i : interaction 상호작용을 하겠다.

-t : terminal 터미널 모드로 사용 (/bin/bash를 자동으로 실행시켜준다.)

--name : 도커 컴포즈를 할 때 컨테이너끼리 결합을 할 때 --link 옵션의 파라미터로 컨테이너 이름을 넘겨주기 위한 식별값.

(도커 컴포즈 뿐만 아니라 하나의 이미지로 여러 컨테이너를 띄울수도 있는데 각각 컨테이너 마다 붙일 수 있는 식별값으로 생각하면 될것 같다.)

 

 

우분투를 해당 옵션으로 다시 실행하기 위해서는 일단 전에 바로 종료되었던 컨테이너를 삭제해야한다.

docker rm $(docker ps -a -q --filter name=myubuntu)

 

실행중인 컨테이너에 접근하는 명령어

docker attach [CONTAINER ID]

-it 옵션으로 컨테이너를 실행하면 /bin/bash프로그램이 실행되는데 그때 우분투에 접속해서 bash 프로그램과 상호작용할 수 있는 명령어이다.

+ Recent posts