당니의 개발자 스토리

1/6 본문

LG CNS/필기노트

1/6

clainy 2025. 1. 6. 10:36

엘리먼트에 직접적으로 뭔가를 하고싶을 때 사용

 

클래스형 컴포넌트에서는 콜백 형태로 사용

createRef는 current 속성을 반드시 붙여줘야한다.

 

DOM 컨트롤 직접하기 위해 사용.

상태변수는 값이 바뀌면 리렌더링.

 

타이머 조작을 할 떄 ref 변수를 써야함

 

카운트를 하나 만들고 카운트를 시작하고 정지하는 버튼을 둘거임

카운트가 1초단위로 올라감

정지버튼 누르면 카운트 올라가는게 중지

 

두 개의 컴포넌트를 만들어볼게요

import "./App.css";
import { useState } from "react";

const CounterWithLocalVariable = () => {
  const [count, setCount] = useState(0);
  let intervalId = 0; // 지역변수
  console.log(`렌더링... count: ${count}, intervalId: ${intervalId}`);

  const startCounter = () => {
    intervalId = setInterval(() => { setCount(count => count + 1) }, 1000);
    console.log(`카운터 시작... intervalId: ${intervalId}`);
  };

  const stopCounter = () => {
    clearInterval(intervalId);
    console.log(`카운터 정지... intervalId: ${intervalId}`);
  };

  return (
    <>
      <p>카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
};

export default () => {
  return (
    <>
      <CounterWithLocalVariable />
    </>
  );
};

1.

startCounter는 setInterval 이라는 함수르 호출함

콜백 함수를 1000 시간 주기로 계속해서 호출해주는 것임

 ms 단위므로 1000 을 주면 1초 주기로 안에잇는 함수 호출

이 함수는 setCount를 증가시키는 작업을 함(1초 단위로)

 

2. 정지를 구현할거임

카운트를 멈추려면 clearInterval을 호출함

clearInterval에는 setInterval을 호출했을 때 반환하는 인터벌 아이디를 넣어줘야 한다.

시작한 타이머의 id 값이 있어야만 중지 가능

 

let intervalID 라는 변수를 정의하고 

setInterval 했을 때의 id 값을 clearInterval 에 넣어주면 된다. 

 

const CounterWithStateVariable = () => {
  const [count, setCount] = useState(0);
  const [intervalId, setIntervalID] = useState(0);
  console.log(`렌더링... count: ${count}, intervalId: ${intervalId}`);

  const startCounter = () => {
    intervalId = setInterval(() => { setCount(count => count + 1) }, 1000);
    setIntervalID(intervalId);
    console.log(`카운터 시작... intervalId: ${intervalId}`);
  };

  const stopCounter = () => {
    clearInterval(intervalId);
    console.log(`카운터 정지... intervalId: ${intervalId}`);
  };

  return (
    <>
      <p>카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
};

 

 

const CounterWithRefVariable = () => {
  const [count, setCount] = useState(0);
  const intervalId = useRef(0);
  console.log(`렌더링... count: ${count}, intervalId: ${intervalId}`);

  const startCounter = () => {
    intervalId = setInterval(() => { setCount(count => count + 1) }, 1000);
    console.log(`카운터 시작... intervalId: ${intervalId.current}`);
  };

  const stopCounter = () => {
    clearInterval(intervalId);
    console.log(`카운터 정지... intervalId: ${intervalId}`);
  };

  return (
    <>
      <p>카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
};

 

토탈 코드

import "./App.css";
import { useState, useRef } from "react";

const CounterWithLocalVariable = () => {
  const [count, setCount] = useState(0);
  let intervalId = 0;
  console.log(`렌더링... count: ${count}, intervalId: ${intervalId}`);

  const startCounter = () => {
    intervalId = setInterval(() => { setCount(count => count + 1) }, 1000);
    console.log(`카운터 시작... intervalId: ${intervalId}`);
  };

  const stopCounter = () => {
    clearInterval(intervalId);
    console.log(`카운터 정지... intervalId: ${intervalId}`);
  };

  return (
    <>
      <p>카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
};

const CounterWithStateVariable = () => {
  const [count, setCount] = useState(0);
  const [intervalId, setInterId] = useState(0);

  console.log(`렌더링... count: ${count}, intervalId: ${intervalId}`);

  const startCounter = () => {
    const id = setInterval(() => { setCount(count => count + 1) }, 1000);
    setInterId(id);
    console.log(`카운터 시작... intervalId: ${intervalId}`);
  };

  const stopCounter = () => {
    clearInterval(intervalId);
    console.log(`카운터 정지... intervalId: ${intervalId}`);
  };

  return (
    <>
      <p>카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
};

const CounterWithRefVariable = () => {
  const [count, setCount] = useState(0);
  const intervalId = useRef(0);

  console.log(`렌더링... count: ${count}, intervalId: ${intervalId.current}`);

  const startCounter = () => {
    intervalId.current = setInterval(() => { setCount(count => count + 1) }, 1000);
    console.log(`카운터 시작... intervalId: ${intervalId.current}`);
  };

  const stopCounter = () => {
    clearInterval(intervalId.current);
    console.log(`카운터 정지... intervalId: ${intervalId.current}`);
  };

  return (
    <>
      <p>카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
};

export default () => {
  return (
    <>
      <CounterWithLocalVariable />
      <CounterWithStateVariable />
      <CounterWithRefVariable />
    </>
  );
};

 

지금 하고 있는 것은 hook에 대해서 보고 있다

클래스형 컴포넌트가 함수형 컴포넌트에 제공해주는 기능들

라이프사이클 메서드를 제공하기 위해서 만들어진 기능이 훅입니다

훅은 함수외부의 어떤 기능들을 정의해놓고 그 기능을 함수 내부에서 쓸 수 있도록 바깥에서 제공해주는 거다

setState 라는 메서드를 가지고 바꿨음

컴포넌트가 가지고 있는 상태변수 값을 바꾸고 render 함수

외부에다가 흐름, 기능들을 별도의 함수로 만드는 거임

 

상태변수를 내가 업데이트하고 값을 바꾸고, 함수를 호출해서 리턴 받아서 뿌리는 거

useState 라는 훅으로 만들어서 제공해주는 거임

 

훅 중에서 useState랑 useEffect, useRef 세 가지를 본거다

굉장히 많이 쓰이는 훅 함수

 

지금부터 하는 훅 함수들은 성능을 향상하기 위해 사용하는 훅 함수라고 보면 됨

useReducer를 보자

 

useReducer

상태 관리 로직이 복잡할 때 useState 대체품으로 사용 

현재 상태와 액션을 받아 새로운 상태를 반환하는 리듀서(reducer) 함수를 통해 상태를 관리

리듀서 함수는 상태 변수하고 액션을 받는 거다.

상태 변경에 필요한 정보

어떤 상태를 어떤 값으로 바꿀거냐 가 핵심임, 이런 것들이 액션에 들어가게 되어있음

 

useReducer는 리듀스 함수와 초기 상태를 인자로 받아서 상태와 디스패치 함수를 반환한다.

  • 리듀스 함수 : 현재 상태와 액션을 받아 새로운 상태를 반환하는 순수 함수 (pure function)
  • 초기 상태 : 상태의 초기값
  • 액션 : 상태를 변경하기 위한 정보가 담긴 객체로, 일반적으로 type 프로퍼티를 가지고 있으며, 필요에 따라 데이터를 포함할 수 있음
  • 디스패치 함수 : 액션을 리듀서로 전달해 상태를 업데이트하는 함수

useReducer의 사용 예

  • 상태 로직이 복잡하거나, 여러 하위 값으로 구성된 상태를 관리해야 할 때
  • 상태 업데이트 로직이 여러 종류의 액션에 의해 다르게 동작해야 할 때
  • 상태와 그 업데이트 로직을 컴포넌트에서 분리하고 싶을 때

 

useState를 이용해서 Counter 컴포넌트를 구현 => App.js

import "./App.css";
import { useState } from "react";

const Counter = () => {
  const [count, setCount] = useState(0);
  const changeCount = e => setCount(count + Number(e.target.innerText));		⇐ 
  return (
    <>
      <div>현재 카운터 값은 <b>{count}</b>입니다.</div>
      <div>
        <button onClick={changeCount}>+1</button>
        <button onClick={changeCount}>-1</button>
      </div>
    </>
  );
};
export default () => <Counter />;

버튼에 있는 값을 가져와서 +1을 하고 싶어요. e.target.innerText 해서 값을 가져와요.

+1 아니면 -1이 문자열로 반환됩니다.

문자열을 + 연산으로 바꾸기 위해서, 숫자 연산으로 바꾸기 위해서는 number 해서 캐스팅을 해주면

버튼에 있는 컨텐츠 값을 가져와서 연산을 하고 

 

이런 식으로 버튼의 라벨을 가져와서 기능을 구현할 수 있어요.

하나의 핸들러 함수를 이용해서 더하기 기능과 빼기 기능을 구현할 수 있다.

위와 같은 코드에 주석만 추가

import "./App.css";

import { useState } from "react";

 

const Counter = () => {

  const [count, setCount] = useState(0);

  const changeCount = e => setCount(count + Number(e.target.innerText));

⇐ 버튼의 내용(contents)이 숫자로 변환 가능하기 때문에 하나의 핸들러 함수로 구현이 가능

  return (

    <>

      <div>현재 카운터 값은 <b>{count}</b>입니다.</div>

      <div>

        <button onClick={changeCount}>+1</button>

        <button onClick={changeCount}>-1</button>

      </div>

    </>

  );

};

export default () => <Counter />;

 

 

버튼의 내용이 숫자로 변환할 수 없는 경우 = 문자열인 경우에는 어떻게 해야할까?

=> 핸들러 함수를 따로 작성해야 한다.

 

e.target.name 을 사용한 경우

 

useReducer를 이용해서 Counter 컴포넌트를 작성해보자.

 

 

reducer 함수는 현재 상태와 그 상태를 변경하는 조건이 매개변수(인자) 값으로 넘어오게 되고,

이것이 INCREMENT면 하나 증가시켜주고, DECREMENT는 감소시켜줌

 

import "./App.css";
import { useReducer, useState } from "react";

// state: 현재 상태 변수의 값
// action: 상태 변수 변경에 필요한 조건과 값 (호출 시 전달되는 값)
function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

const Counter = () => {
  /*
  const [count, setCount] = useState(0);
  const changeCountPlus = () => setCount(count + 1);
  const changeCountMinus = () => setCount(count - 1);
  */
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <>
      <div>현재 카운터 값은 <b>{state.count}</b>입니다.</div>
      <div>
        <button onClick={() => dispatch({ type: "INCREMENT" })}>하나 더하기</button>
        <button onClick={() => dispatch({ type: "DECREMENT" })}>하나 빼기</button>
      </div >
    </>
  );
};
export default () => <Counter />;

디폴트는 현재 상태를 return 한다. 

현재 상태 변수의 값이 전달되는 것.

action은 상태 변수 변경에 필요한 조건과 값 (호출 시 전달되는 값)

 

useReducer를 사용할 때 dispatch 함수상태를 변경하기 위한 명령을 리듀서에 전달하는 역할을 합니다.

 

리듀스 함수를 만들고 상태 변수 대신에 디스패치

const[state, dispatch] = useReducer(reducer, { count : 0});

리듀스 함수랑 count는 상태 변수. 

나는 상태변수는 카운트 하나를 유지하는 거고 카운트의 초기값은 0

현재 카운트는 상태 변수, state에 있는 카운트예요.

내가 하나 더하기를 하면 dispatch 함수를 호출해요

매개변수로 상태변수랑 액션이 들어가는데 action을 내가 넣어줍니다

 

리듀스 함수는 현재 상태를 어떻게 바꿀 건지 매개변수로 가진다

타입에 따라서 상태변수를 변경하는 로직이 달라지는 겁니다.

 

디스패치는 넘겨줄 리듀스 함수를 호출해서 새로운 상태를 state에 덮어쓰고

자기 자신을 다시 호출해서 렌더링이 일어나도록 해준다는 겁니다.

상태변수의 관리하고자 하는 값이 있으니까 state.count 해서 쓰면 된다.

디스패치 함수를 호출

 

안에서 동작을 구분할 수 있는 매개변수로 넣어줘야함.

 

어떤 장점이 있느냐?

상태변수를 정의하는 부분하고 상태 변수를 변경하는 로직이 분리되었음

지금은 바깥쪽에 있음

이거를 만약에 별도의 함수, reducer.js 에다가 가져다 놨다고 해봐요.

이걸 넣고 export default 시킵니다

 

그래서 App.js에 import reducer from "./reducer"; 해서 써도 된다.

 

위에서 봤던

 

useReducer의 사용 예

  • 상태 로직이 복잡하거나, 여러 하위 값으로 구성된 상태를 관리해야 할 때
  • 상태 업데이트 로직이 여러 종류의 액션에 의해 다르게 동작해야 할 때
  • 상태와 그 업데이트 로직을 컴포넌트에서 분리하고 싶을 때

 

2교시

 

위의 코드를 보면 useReduce 함수를 쓸 때는 상태변수가 어떤 값을 가지는지,

리듀스 함수를 정의하고 디스패치 함수는 내가 상태변수를 변경을 요청하는 함수이다.

객체형태로 던져주면 된다.

리듀스 함수의 액션으로 들어간다.

state는 useReducer가 알아서 넣어준다.

 

useState를 사용해서 여러 입력창의 상태를 관리

 

 

return 합니다.

 

밑에는 인풋 박스가 들어갈 거임

인풋 타입은 텍스트

밑에도 인풋 타입은 텍스트

내가 여기에 이름, 별명 넣으면 위에 이름과 별명이 찍힌다.

 

어떤 값이 바뀌면 화면에 반영되어야 한다. 상태변수 2개

useState 해서 초기값을 줍니다.

const 닉네임, useState해서 만듭니다.

 

const 해서 changeName은 이벤트 객체가 들어오면  setName 한다. e.target.value 해서

이렇게 정의한 상태변수와 이벤트 핸들링 함수를 아래에다가 집어볼까요?

네임 닉네임 {} 안에 넣어주고

onChange 했을 때 changeName ㅎ주면 된다,

changeNickName

useState 정리하면 그것을 막도록 이벤트 핸들러를 설정하면 되겠죠.

 

useReducer를 사용하는 것으로 변경

const reducer는 함수이다. 

매개변수로 state 하고 action을 받는다. state는 현재상태

action은 변경에 필요한 정보들, 조건과 값

 

변수를 객체 형태로 들여오므로 값은 내가 이름을 부여하기 나름

일반적으로 type을 써서 어떤 값으 변경할지

 

action은 { type  변경할 상태변수, value: 변경할 값} 가 지정될 거다

디스패치 함수가 호츨딜 때 매개변수, 인자값으로 전달 

 

 

디스패치함수를 호출

디스패치 함수는 액션을 가져감

type은 식별자로 썼었는데 value는 이벤트 객체 가 들어와야 타겟.밸류를 넘겨줄 수 있음

이넵트 ㅋ객체로 

nickName   name

action type이 모예요. 네임 아니면 닉네임이잖아

 

value로 넘은 값으로 업데이트 하는거죠

 

import "./App.css";

import { useReducer } from "react";

 

const reducer = (state, action) => {

  return { ...state, [action.type]: action.value };

};

 

const Info = () => {

  const [state, dispatch] = useReducer(reducer, { name: "", nickName: "" });

  const { name, nickName } = state;

  const changeValue = e => dispatch({ type: e.target.name, value: e.target.value });

  return (

    <>

      <div>

        <p>이름: {name}</p>

        <p>별명: {nickName}</p>

      </div>

      <div>

        <p>이름: <input type="text" name="name" value={name} onChange={changeValue} /></p>

        <p>별명: <input type="text" name="nickName" value={nickName} onChange={changeValue} /></p>

      </div>

    </>

  );

};

 

export default () => <Info />;

 

이 부분이 action의 type 하고 똑같다.

 

 

 

 

 

useMemo

성능 최적화를 위해 특정 값이 변경될 때만 메모이제이션된 값을 재계산하도록 하여 불필요한 계산을 방지

계산 비용이 높은 작업이나 렌더링 중에 자주 호출되는 작업에 유용

 

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  ~~~~~~

                              계산을 수행할 함수                 의존성 배열 

useMemo로 전달된 함수는 렌더링이 일어나는 동안 실행 ⇒ 일반적으로 렌더링이 일어나는 동안 실행해서는 안 될 작업을 useMemo() 함수에 넣으면 안 됨 

 

의존성 배열을 넣지 않을 경우, 렌더링이 일어날 때마다 매번 함수를 실행


App.js 파일에 Average 컴포넌트를 추가


useMemo 훅을 이용해서 리스트에 숫자가 등록된 경우에만 평균값을 계산하도록 수정

 

 

3교시

 

useCallback

 

useMemo 훅과 유사하게 성능 최적화를 위해 사용되는 훅. useCallback은 콜백 함수가 불필요하게 다시 생성되는 것을 방지

컴포넌트가 리렌더링될 때 동일한 콜백 함수가 사용되도록 함.

콜백 함수가 자식 컴포넌트의 props로 전달되는 경우 유용하다.

 

const memorizedCallback = useCallback( ) => { ... }, { dependency, ...]);

                                                                      ~~~~~~~~   ~~~~~~~~~~~~~

                                                                          콜백함수          의존성 배열 -> 배열의 값이 변경될 때만 콜백 함수가 새로 생성

 

useCallback을 사용하지 않고, props로 전달되는 함수가 재정의되어 자식 컴포넌트가 리렌더링 되는 것을 확인

 

props로 전달되는 함수가 재정의되어 자식 컴포넌트가 리렌더링 되는 것을 확인

 

 

Todos 라는 컴포넌트가 들어가고,

 Todos는 이런것을 리턴하는 놈이다.

 

Todos를 호출하고 줄 하나 그어주고 버튼을 넣을건데 카운트 증가 라는 버튼을 넣을 거예요.

 

const increment = () 현재 카운트의 + 1한 값을 setCount 할 거예요.

onClick 하면 increment 넣어주고, 내가 {count}를 넣어줍니다.

 

Todos는 새로운 할 일 목록을 가지고 있고, 할일 목록의 내용을 출력. 할일 목록은 부모 컴포넌트에 있음.

 

새 할일을 넣어줌. 할 일을 하나씩 하나씩 추가해주는 함수예요.

우리가 이 Todos 쪽으로 addTodo를 전달할 겁니다.

Todos todos를 넘겨주고, addTodo 해서 addTodo를 넘겨줄 거예요.

 

key는 인덱스가 되는 것이고, 그 내용은 Todo가 된다.

목록이 나온다.

 

버튼

Add Todo

 

 

카운트 증가를 누르면 카운트가 증가되고

add Todo를 누르면 할일이 추가된다.

 

console.log 해서 child 렌더링 해줍니다.

 

f5해서 카운트 증가를 눌렀어요. 카운트 증가를 눌렀더니 child 컴포넌트 

누를 때마다 호출이 되더라.

바뀌고 있는 것은 카운트만 바뀌고 있음.

Todos가 새롭게 그려진다는 거다.

Todos는 Todos나 addTodo가 바뀌는 경우에만 그려졌으면 좋겠어,

얘가 새롭게 그려지는 게 아니라, +값이 바꼈을 때만 그려졌으면 좋겠다는 거다.

 

리액트가 가지고 있는 memo 라는 함수를 가지고 이 전체를 둘러싸준다.

리액트의 memo 함수는 로 컴포넌트를 매핑하면 컴포넌트를 렌더링할 때, 그 결과를 메모리징 한다.

다음 렌더링이 일어날 때, props가 같으면 메모리징 된 내용을 재사용한다.

 

Todos라는 함수는 안에 있는 내용이 안 바뀜.

Todos나 addTodo 라는 props 값이 같은 값이 들어온다면 앞에 실행된 결과를 리액트가 기억하고 있다가

props 변수가 안 바꼈다면 앞에 를 그대로 반환.

 

여기서 여기까지의 실행 결과를 기억하고 있다가 기억하고 있던 내용을 그대로 기억하고 있다가 반환해줌으로써

이 함수의 실행 시간을 줄여주는 거다.

 

그게 이 Memo 라는 함수가 해주는 역활이다.

 

그러면 이제 우리가 count가 증가할 때 이  log가 안 나오는게 맞겠죠.

이대로라면 addTodo는 렌더링이 되면서 그때마다 addTodo는 새롭게 만들어짐

이 함수는 우리가 setter를 써가지고 상태변수를 바꾸면 이 함수를 호출할 때마다 이 addTodo는 그때마다 새롭게 만들어진다.

 

useEffect를 통해서 필요할 때만 새롭게 만들어지도록 한다.

 

Memo는 props 변수가 바뀌지 않으면 해당 함수의 실행 결과를 가지고 잇다가 그 가지고 있는 값을 사용하도록

메모리징 해주는 것이다.

Tdos는 바뀌지 않지만, 이 App.js이 렌더링 될 때마다

함수의 내용이 변경되지 않았음에도 Todos 컴포넌트를 렌더링 하고 있어요.

addTodo props만 남겨두고 Todos props를 제거해서 확인할 수가 있어요.

 

렌더링이 일어나는 것은 카운트가 일어나기 때문에 변경이 없다.

변경이 없는 데도 불구하고 매번 얘가 실행된다.

 

 

매번 새롭게 만들어지도록 해야만 여기에 나오는 memo가 제대로 동작

useCallback을 이용해서 함수가 props 변수로 전달되는 함수가 매번 재정의 되지 않도록 수정하는 겁니다.

 

useCallback 함수를 넣어주고 , 해서 의존성 배열

Todos가 바뀔 때 카운트를 증가할 때는 log가 안 나오고 있고

addTodo 하면 함수가 재정의되고 렌더링 되고

카운트가 증가할 때는 안 나오고

이거 누를 때만 나오는 거예요.

 

 

마운트 되었을 때만 한번만 나오는 거예요.

의존성 배열을 안 써도 관계 없죠.

왜 안써도 관계 없어요?

 

이 함수의 내용은 안 바뀌는 거죠.

그리고 자식 컴포넌트가 렌더링 되는 조건은 addTodo도 있고 Todos도 있죠.

얘는 useCallback에서 의존성 배열을 안 넣어줘도 되는 거예요.

 

보통 책에는 memo를 안 쓰고 props로 넘어가는 함수를 useCallback으로 묶어주는데

큰 의미가 없죠.

 

이 memo를 안 쓰면 props를 

 

useCallback을 잘 알아두시고,

 

useContext 마지막 훅 함수이다

 

useContext는 함수형 컴포넌트에서 Context API를 쉽게 사용할 수 있도록 도와주는 훅

 

Context API

컴포넌트 트리에서 전역적으로 데이터를 공유할 수 있도록 하는 방법을 제공해준다.

props 드릴링 이라고 합니다.

드릴링 문제를 해결할 수 있어요.

부모 컴포넌트에서 자식 컴포넌트로 데이터를 반복적으로 전달하는 props drilling 문제를 해결할 수 있다.

 

<A>

          <B>

                    <C>

                              <D></D>

                    </C>

           </B>

</A>

 

이현상을 props drilling 이라고 합니다.

context api를 쓰면 어디서든지 가져가서 쓸 수 있다. 그게 이제 컨텍스트 api가해주는 역할임.

 

 

useContex의 역할

Context를 구독 (subscribe) 해서 해당 Context의 현재 값을 가져오는 데 사용된다.

전역적인 데이터 저장소를 만들고 그 컨텍스트에 있는 값을 받아봄

서브 

 

컴포넌트가 컨텍스트 값을 사용하면 컨텍스트의 값이 변경될 때 자동으로 해당 컴포넌트가 리렌더링 된다.

컨텍스트에 있는 값이 바뀌면 컨텍스트의 값을 수신하는 놈은 리렌더링 된다.

 

 

사용법

 

컨텍스트를 먼저 만들어요

컨텍스트의 프로바이더를 정의하고

컨텍스르를 소비한다 라고 합니다.

 

생성은 리액트에서 제공하는 createContext 함수를 사용합니다. => React.createContext()를 사용해서 context를 생성

정의는 Context.Provider라는 컴포넌트를 사용해서 context 값을 하위 컴포넌트로 전달

소비는 useContext 훅을 사용해서 context 값을 가져와서 사용

 

테마가 적용된 페이지를 생성

ThemedButton, Blog, News 컴포넌트를 생성

여기에 테마 변경이라는 버튼을 클릭하면 버튼의 배경색과 글자색을 변경

 

App.js에 만들어볼게요.

 

 

 

상태변수의 값을 바꾸는 핸들러 함수를 만들었어요.

버튼에다가 onClick 했을 때 저기에 나오는 changeTheme을 호출하도록

그리고 button에다가 style을 줄 거다.

backgroundColor는 theme가 Light이면 #fff(흰색)이고 dark이면 #333(회색)으로 할 거다.

 color는 테마가 

테마변경 하면 검정색과 흰색

 

똑같이 동작을 하죠. 블로그하고 뉴스도 테마를 전달할 거예요.

테마를 바꾸는 기능은 없기 때문에 현재 테마만 전달합니다.

 

블로그가 테마를 props 변수로 받아서 전체에 대해서 스타일을 처리합니다.

backgroundColor

color.theme ===

 

똑같이 news도 

테마를 props 변수로 받아와야겠죠.

blackDark 테마일 때는 검정 배경의 흰색 글자

흰 배경의 검정 글자

 

이 테마가 만약에 app 밑에 블로그도 있고 뉴스도 있고 한데

이거 자체가 다른 컴포넌트에 의해서 묶여있으면

복잡한 구조를 가지고 있으면 저 props를 전달해야하는 문제가 생긴다

props 변수로 던져주고

이거를 받아와서 props 변수를 쓸 수가 있다

 

app 아래에 

 

블로그와 뉴스를 포함하는 컨텐츠 컴포넌트가 있으면 컴포넌트를 추가해볼게요,

컨텐츠 안에 있다는 거예요. 컨텐츠 해가지고 리턴

 

props 변수로 테마를 받아야함

받은 테마를 블로그 쪽으로 테마를 전달

news에 테마 해서 테마를 전달

 

Blog와 News를 포함하는 Contents 컴포넌트를 추가 ⇒ Contents 컴포넌트는 theme 변수를 필요로 하지 않지만, 하위 컴포넌트에게 전달하기 위해서 props 변수로 받아서 처리 

import { useState } from "react";

import "./App.css";

 

const ThemedButton = ({ theme, changeTheme }) => {

  /*

  const [theme, setTheme] = useState("light");

  const changeTheme = () => setTheme(theme === "light" ? "dark" : "light");

  */

  return (

    <button onClick={changeTheme}

      style={{

        backgroundColor: theme === "light" ? "#fff" : "#333",

        color: theme === "light" ? "#000" : "yellow"

      }}>테마 변경</button>

  );

};

 

const Blog = ({ theme }) => {

  return (

    <div style={{

      backgroundColor: theme === "light" ? "#fff" : "#333",

      color: theme === "light" ? "#000" : "#fff"

    }}>

      <h1>블로그</h1>

      <hr />

      <h2>블로그 제목</h2>

      <p>블로그 내용</p>

    </div>

  );

};

 

const News = ({ theme }) => {

  return (

    <div style={{

      backgroundColor: theme === "light" ? "#fff" : "#333",

      color: theme === "light" ? "#000" : "#fff"

    }}>

      <h1>뉴스</h1>

      <hr />

      <h2>뉴스 제목</h2>

      <p>뉴스 내용</p>

    </div>

  );

};

 

const Contents = ({ theme }) => {

  return (

    <>

      <Blog theme={theme} />

      <News theme={theme} />

    </>

  );

};

 

export default function App() {

  const [theme, setTheme] = useState("light");

  const changeTheme = () => setTheme(theme === "light" ? "dark" : "light");

 

  return (

    <div>

      <h1>테마가 적용된 페이지</h1>

      <ThemedButton theme={theme} changeTheme={changeTheme} />

      <Contents theme={theme} />

    </div>

  );

}

 

리액트 안에 create

 

const 해서 테마하고 changeTheme 두 개를 컨텍스트를 통해서 제공

 

 

return 하는 이 구문에 ThemeProvider로 감싸고, props 변수를 삭제한다.

const {theme, changeTheme} = useContext(ThemeContext);

 

 

블로그도 더이상 props 변수로 받지 않아도 돼요.

컨텍스트 소비

theme 변수만 필요하니까 useContext

 

 

컴포넌트를 파일(모듈)로 분리

ThemeContext.js

 

ThemeProvider.js

import { useState } from "react";

import ThemeContext from "./ThemeContext";

 

const ThemeProvider = ({ children }) => {

    const [theme, setTheme] = useState("light");

    const changeTheme = () => setTheme(theme === "light" ? "dark" : "light");

 

    return (

        <ThemeContext.Provider value={{ theme, changeTheme }}>

            {children}

        </ThemeContext.Provider>

    );

};

export default ThemeProvider;



ThemedButton.js

import { useContext } from "react";

import ThemeContext from "./ThemeContext";

 

const ThemedButton = () => {

    const { theme, changeTheme } = useContext(ThemeContext);

 

    return (

        <button onClick={changeTheme}

            style={{

                backgroundColor: theme === "light" ? "#fff" : "#333",

                color: theme === "light" ? "#000" : "yellow"

            }}>테마 변경</button>

    );

};

export default ThemedButton;



Blog.js

import { useContext } from "react";

import ThemeContext from "./ThemeContext";

 

const Blog = () => {

    const { theme } = useContext(ThemeContext);

 

    return (

        <div style={{

            backgroundColor: theme === "light" ? "#fff" : "#333",

            color: theme === "light" ? "#000" : "#fff"

        }}>

            <h1>블로그</h1>

            <hr />

            <h2>블로그 제목</h2>

            <p>블로그 내용</p>

        </div>

    );

};

export default Blog;

 

 

 

 

App.js를 ThemeProvider 컴포넌트를 사용하지 않고 Provider를 직접 정의하는 방식으로 변경

 

아래 코드의 상태변수와 상태변수를 변경하는 함수를 App에 정의하고, 하위 컴포넌트에서 useContext 훅을 이용하는 방법으로 변경

 

function Parent()

props 변수로 전달하는 또는 전달 받는 코드를 삭제하고,

useContext() 훅을 이용해서 필요한 컨텍스트 변수를 추출해서 사용

 

const {count, resetCount} = useContext(CounterContext)

 

fucntion Child() {

 

 

}

plusOne 이라고 썼고 addCount 로 되어있으니까 

 

수정 결과 이렇게 만들어진다.

 

LAB2

 

컨텍스트 변수 만들고

프로바이더 만들고

프로바이더 적용하고

useContext로 바꿔주기

 

순서를 잘 맞춰야된다. 순서대로 안 하면 헷갈린다.

 

reduce 함수로 

 

const [state, handlerChange] = useInputs( { name: '', nickname: '' });

상태 변수하고 그 상태변수 값을 변경하는 핸들러 함수를 반환한다.

useInputs 사용자 정의 훅 함수를 만든다.

 

테마 프로바이더로 감싸기만 하면 된다.

themedButton을 임포트 하고,

컨텐츠를 임포트 하고 

useReducer는 reducer 하고 initState 객체들을 관리할 수 있다.

반환해줘야 하는 핸들러 change는 이벤트 객체가 오면

dispatch를 통해서 바꾸면 된다.

 

reducer 함수 쪽으로 던지는 

변경된 상태가 내려온다

그러면은 그 변경된 상태를 이 쪽으로 return 하면 된다.

 

상태변수와 상태변수의 값을 변경하는 핸들러 함수를 반환하는 useInputs 사용자 정의 훅 함수를 생성

 

import { useReducer, useState } from "react";

 

function reducer(state, action) {

  return {

    ...state,

    [action.name]: action.value

  };

}

 

function useInputs(initState) {

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

  const handlerChange = e => {

    dispatch(e.target);

  };

  return [state, handlerChange];

}

 

function Info() {

  /*

  const [state, dispatch] = useReducer(reducer, { name: '', nickname: '' });

  const { name, nickname } = state;

  const handlerChange = (e) => {

    dispatch(e.target);

  };

  */

 

  const [state, handlerChange] = useInputs({ name: '', nickname: '' });

  const { name, nickname } = state;

 

  return (

    <>

      <div>

        <p>이름: {name}</p>

        <p>별명: {nickname}</p>

      </div>

      <div>

        <p>이름: <input type="text" value={name} name="name" onChange={handlerChange} /></p>

        <p>별명: <input type="text" value={nickname} name="nickname" onChange={handlerChange} /></p>

      </div>

    </>

  );

}

 

const App = () => {

  return (

    <>

      <Info />

    </>

  );

};

 

export default App;

 

 

일정 관리 앱 개발 - 리액트의 필수 코스

프로젝트 생성

 

index.css 파일이 전체적으로 적용되는 스타일 시트

 

import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoTemplate from './components/TodoTemplate';

function App() {
  return (
    <TodoTemplate>
      <TodoInsert />
    </TodoTemplate>
  );
}

export default App;

 

 

import "./TodoTemplate.css"

const TodoInsert = () => {

    return (
        <form>
            <input type="text" placeholder="할 일을 입력하세요." />
            <button type="submit"></button>
        </form>
    );
}
export default TodoInsert

 

내가 단축키를 누르면 그 단축키가 눈에 보이는(화면에 띄워지는) 프로젝트 해보기(강의에서 나온건 아니고 그냥 한번 혼자 생각해봄)

 

버튼도 removeCircle 이라는 거를 쓸 거다.

style을 입혀보겠습니다.  TodoListItem.css에 가셔서

 

맨 바깥쪽에 둘러싸인 놈.xxx 

먼저 padding 해서 1rem

 

f: 

'LG CNS > 필기노트' 카테고리의 다른 글

리액트 컴포넌트 만들기 실습  (0) 2025.01.09
12/27  (0) 2025.01.08
12월 26일 (목)  (0) 2025.01.05
12/24 화  (0) 2024.12.24
12/23 월  (0) 2024.12.23