중복코드 줄여보자~~~

input을 만들었을 경우

 

App.jsx

import React from "react";
import { useState } from "react";

function App() {
	const [name, setName] = useState('')
    const [password, setPassword] = useState('')
    
    const onChangeNameHandler = (e) => {
    	setName(e.target.vaule)
    }
    
    const onChangePasswordHandler = (e) => {
    	setPassword(e.target.vaule)
    }
    
    return <div>
    	<input type="text" value={name} onChange={onChangeNameHandler} />
        <input type="text" value={name} onChange={onChangePasswordHandler} />

hooks > useInput.js

cosnt useInput = () => {
	// state
    const [value, setValue] = useState('')
    
    // handler
    const handler = (e) => {
    	setValue(e.target.value)
    }
    
    return [value, handler];
}

export default useInput;

useInput 커스텀 훅 불러오기!

import React from "react";
// import { useState } from "react";
import useInput from "./fooks/useInput";

function App() {
	// 이렇게만 만들어도 밑에서 다 쓸 수 있다.
	const [name, onChangeNameHandler] = useInput('')
    const [name, onChangePasswordHandler] = useInput('')
    
    // const [name, setName] = useState('')
    // const [password, setPassword] = useState('')
    
    // const onChangeNameHandler = (e) => {
    //	   setName(e.target.vaule)
    // }
    
    // const onChangePasswordHandler = (e) => {
    //	   setPassword(e.target.vaule)
    // }
    
    return <div>
    	<input type="text" value={name} onChange={onChangeNameHandler} />
        <input type="text" value={name} onChange={onChangePasswordHandler} />

'React' 카테고리의 다른 글

Redux-thunk (미들웨어)  (0) 2023.04.29
axios interceptor  (0) 2023.04.29
비동기 통신 - axios(get)  (0) 2023.04.28
React Hooks - 최적화(React.memo, useCallback, useMemo)  (1) 2023.04.19
React Hooks - useRef  (0) 2023.04.19

미들웨어란?

리덕스에서 dispatch를 하면 action 이 리듀서로 전달이 되고, 리듀서는 새로운 state를 반환한다. 근데 미들웨어를 사용하면 이 과정 사이에 우리가 하고 싶은 작업들을 넣어서 할 수 있다.

카운트 프로그램에서 더하기 또는 빼기 버튼을 바로 하지 않고 3초를 기다렸다가 더하는 것, 3초를 기다리는 작업이 미들웨어가 해주는 것이다.

Thunk

리덕스에서 많이 사용하고 있는 미들웨어중에 하나이다. thunk를 사용하면 우리가 dispatch를 할때 객체가 아닌 함수를 dispatch 할 수 있게 해준다. 즉 dispatch(객체) 가 아니라 dispatch(함수)를 할 수 있게 되는 것이다!

중간에 우리가 하고자 하는 작업을 함수를 통해 넣을 수 있고, 그것이 중간에 실행이 되는 것 이다. 그래서 아래 흐름과 같이 실행이 되며, 이 함수를 thunk 함수라고 부른다.

dispatch(함수) → 함수실행 → 함수안에서 dispatch(객체)

Thunk 사용하기

1. 우리의 첫 thunk 함수 만들기 : createAsyncThunk
    - reduxToolkit 내장 API
2. creatSlice => extraReducer에 thunk 등록하기
3. dispatch(thunk 함수) 하기
4. 테스트

 

😎3초를 기다리게 해보자!

redux > modules > counter.js

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
// 2개의 INPUT이 들어감
// (1) 이름 : 의미는 크게 없음
// (2) 함수
export const __addNumber = createAsyncThunk(
    'ADD_NUMBER_WAIT',
    (payload, thunkAPI) => {
        // 수행하고싶은 동작: 3초를 기다리게 할 예정
        setTimeout(() => {
            thunkAPI.dispatch(addNumber(payload))
        }, 3000)

    }
)

여기서 Thunk의 기능은 3초를 기다리게 한다.

 

App.js

import { __addNumber } from './redux/modules/counter';
  const addNumberBtn = () => {
    // dispatch(addNumber(+number))
    dispatch(__addNumber(+number))
  }

 

동작

+number로 addNumber가 들어오면 counter.js에 있는 payload로

(payload, thunkAPI)

addNumber에 넣어줬던 +number값이 들어오게 된다.

예를들어 100이라고 한다면,

그 값이 payload로 들어온다.

thunkAPI.dispatch(addNumber(payload))

들어와서 3초후에 밑에 있는 디스패치의 action.payload에 100이라는 값이 들어가면서 state가 업데이트 된다.

const counterSlice = createSlice({
    name: "counter",
    initialState,
    reducers: {
      addNumber: (state, action) => {
        state.number = state.number + action.payload;
      },

정리

  • 리덕스 미들웨어를 사용하면, 액션이 리듀서로 전달되기전에 중간에 어떤 작업을 더 할 수있다.
  • Thunk를 사용하면, 객체가 아닌 함수를 dispatch 할 수 있게 해준다. [thunk의 핵심]
  • 리덕스 툴킷에서 Thunk 함수를 생성할 때는 **createAsyncThunk 를 이용한다.**
  • **createAsyncThunk() 의 첫번째 자리에는 action value, 두번째에는 함수가 들어간다.**
  • 두번째로 들어가는 함수에서 2개의 인자를 꺼내 사용할 수 있는데, 첫번째 인자는 컴포넌트에서 보내준 payload이고, 두번째 인자는 thunk에서 제공하는 여러가지 기능이다.

'React' 카테고리의 다른 글

Custom Hooks  (1) 2023.05.01
axios interceptor  (0) 2023.04.29
비동기 통신 - axios(get)  (0) 2023.04.28
React Hooks - 최적화(React.memo, useCallback, useMemo)  (1) 2023.04.19
React Hooks - useRef  (0) 2023.04.19

환경정보로 빼기

.env파일 만들고 저장!

REACT_APP_SERVER_URL=http://localhost:주소번호(ex.4005)

 원래 있던 URL대신 (`${process.env.REACT_APP_SERVER_URL}/todos`) 입력한다.

// 조회 함수
  // 비동기 함수 : 서버(json-server)에 todos를 요청하는 함수
  const fatchTodos = async () => {
    //const {data} = await axios.get("http://localhost:번호/todos")
    //console.log('data', data)
    const {data} = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`)
    setTodos(data)
  };

 

 

axios폴더 > api.js파일 만들고 저장!

import axios from "axios";

const instence = axios.create({
    baseURL: process.env.REACT_APP_SERVER_URL,
})

export default instence;

axios를 api로  바꾸고 괄호안에 ("/todos")로 변경하면 된다.

const fatchTodos = async () => {
    // 1. const {data} = await axios.get("http://localhost:번호/todos")
    // console.log('data', data)
    // 2. const {data} = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`)
    // setTodos(data)
    const {data} = await api.get("/todos")
    setTodos(data)
  };

이제는 가공한 인스턴스를 조금 더 interceptor를 이용해서 좀 더 요청과 응답 사이에 관여해보자~~

request와 response 사이에 어떤 로직들을 넣겠다.

instence.interceptors.request.use(

    // 요청을 보내기 전 수행되는 함수
    function(config){
        console.log('인터셉터 요청 성공!')
        return config;
    },

    // 오류 요청을 보내기 전 수행되는 함수
    function(error){
        console.log('인터셉트 요청 오류!')
        return Promise.reject(error);
    },
)

instence.interceptors.request.use(

    // 응답을 내보내기 전 수행되는 함수
    function(response){
        console.log('인터셉터 응답 받았습니다!')
        return response;
    },

    // 오류 응답을 내보내기 전 수행되는 함수
    function(error){
        console.log('인터셉터 응답 오류 발생!')
        return Promise.reject(error);
    },
)

인자는 항상 (config)라는 인자를 받는다. 매개변수로 들어오는 이름은 상관없다.

오류 함수 return에 Promise.reject를 꼭 해줘야 한다.

인터셉터 요청, 응답 성공

컴포넌트가 렌더링 되면서 fetchTodos가 실행이 되면서 axios를 콜 한다. 그러면서 requset를 하게 됩니다.

이렇게 중간에서 어떤 인터셉터가 HTTP 요청을 가로채서 어떠한 작업을 하는 것이다.

강제로 실패시키기

timeout이란?

axios 콜을 했을 때, 서버에 통신을 요청하면 '나는 언제까지 기다린다'라는 초(ms)를 말한다.

예를 들면 '2초까지 기다릴거야'라고 했을 때 2(0.002ms->2000을 적어줘야 2초가 됨)초를 기다렸는데도 안오면 오류를 내버리는 기준을 말한다.

const instence = axios.create({
    baseURL: process.env.REACT_APP_SERVER_URL,
    timeout: 1,
})

이렇게 변경하면, 요청 타임아웃이 1ms이다. 엄청 짧은 시간,,

뭐야 응답받으면 안되는데;;  왜 오류메세지는 떠...?

 

'React' 카테고리의 다른 글

Custom Hooks  (1) 2023.05.01
Redux-thunk (미들웨어)  (0) 2023.04.29
비동기 통신 - axios(get)  (0) 2023.04.28
React Hooks - 최적화(React.memo, useCallback, useMemo)  (1) 2023.04.19
React Hooks - useRef  (0) 2023.04.19

*설치할 것

yarn add axios
yarn add json-server

json-server --watch db.json --port 4000를 입력했더니
bash: json-server: command not found
떠서
npm install -g json-server 하고 다시

json-server --watch db.json --port 4000
썻더니 적용됐다
머여,,,; 이유는 모르겠다..

그럼 인사해준다 ㅋ.ㅋ

db.json 폴더를 하나 만들고 넣어준다.

{
    "todos": [
        {
            "id": 1,
            "title": "react"
        },
        {
            "id": 2,
            "title": "node"
        },
        {
            "id": 3,
            "title": "spring"
        }
        
    ] 
}
function App() {

  const fatchTodos = async () => {
    const response =  axios.get('http://localhost:4000/todos')
    console.log('response', response)
  }

  useEffect(() => {
    // db로부터 값을 가져올 것이다.
    fatchTodos()
  }, [])
  

  return (
    <div>
      axios
    </div>
  );
}

export default App;

여기서 consloe.log 확인을 하면 

pending이 찍힌 이유는 axious 요청을 한 직후에 찍혀 버리기 때문에 axious앞에 await문법 쓰면 async블록 안에서 

const {data} = await axios.get("http://localhost:4000/todos")

줄이 끝날 때까지 기다려준다 -> 정상적으로 값을 받을 수 있다.

여기서 data를 빼오자~~~~!

컴포넌트가 렌더링이 됐을 때 비동기함수가 실행되기 전에 return부분이 호출이 된다.

렌더링이 되고 나서 비동기함수가 async니까 타이밍이 더 늦다.

코드는 위에서부터 돌아가지만 async이기 때문에 fatchTodos 이 부분이 돌아갈 때까지 return이 기다리지 않는다.

그래서 todos는 없을수도 있는 현재 null일 수 있다. 그래서 todos? 해주면 된다!

'React' 카테고리의 다른 글

Redux-thunk (미들웨어)  (0) 2023.04.29
axios interceptor  (0) 2023.04.29
React Hooks - 최적화(React.memo, useCallback, useMemo)  (1) 2023.04.19
React Hooks - useRef  (0) 2023.04.19
React Hooks - useEffect  (0) 2023.04.19

(1) 리-렌더링의 발생 조건

  1. 컴포넌트에서 state가 바뀌었을 때
  2. 컴포넌트가 내려받은 props가 변경되었을 때
  3. 부모 컴포넌트가 리-렌더링 된 경우 자식 컴포넌트는 모두

(2) 최적화

리액트에서 리렌더링이 빈번하게, 자주 일어난다는 것은 그렇게 좋은 소식은 아니ek. 비용이 발생하는 것은 최대한 줄여야 한다. 이런 작업을 우리는 최적화(Optimization).

리액트에서 불필요한 렌더링이 발생하지 않도록 최적화하는 대표적인 방법은

  • memo(React.memo) : 컴포넌트를 캐싱
  • useCallback : 함수를 캐싱
  • useMemo : 값을 캐싱

memo(React.memo)

: 리-렌더링의 발생 조건 중 3번째 경우. 즉, 부모 컴포넌트가 리렌더링 되면 자식컴포넌트는 모두 리렌더링 된다.

부분을 돕는 도구가 바로 React.memo!!

컴포넌트를 메모리에 저장해두고 필요할 때 갖다 쓰게 됩니다. 이렇게 하면 부모 컴포넌트의 state의 변경으로 인해 props가 변경이 일어나지 않는 한 컴포넌트는 리렌더링 되지 않아요. 이것을 컴포넌트 memoization 이라고 한다.

export default React.memo(Box1);
export default React.memo(Box2);
export default React.memo(Box3);

useCallback

: 인자로 들어오는 함수 자체를 기억(메모이제이션)

?? : React.memo를 통해서 Box1.jsx는 메모이제이션을 했는데도 리렌더링이 되네요?

네, 그것은 우리가 함수형 컴포넌트를 사용하기 때문이고 App.jsx가 리렌더링 되면서 코드가 다시 만들어지기 때문이다.

자바스크립트에서는 함수도 객체의 한 종류에요. 따라서 모양은 같더라도 다시 만들어지면 그 주솟값이 달라지고 이에 따라 하위 컴포넌트인 Box1.jsx는 props가 변경됐다고 인식한다.

const onInitButtonClickHandler = () => {
  initCount();
};

이 함수를 메모리 공간에 저장해놓고, 특정 조건이 아닌 경우엔 변경되지 않도록 하려면?

// 변경 후
const initCount = useCallback(() => {
  setCount(0);
}, []);

count를 초기화 할 때, 콘솔을 찍으려멱?

...

// count를 초기화해주는 함수
const initCount = useCallback(() => {
  console.log(`[COUNT 변경] ${count}에서 0으로 변경되었습니다.`);
  setCount(0);
}, []);

...

: [COUNT 변경] 0에서 0으로 변경되었습니다.

이런 현상이 발생하는 이유는, useCallback이 count가 0일 때의 시점을 기준으로 메모리에 함수를 저장했기 때문이다. 이 때문에 우리는 dependency array가 필요하다.

기존 코드의 dependency array에 count를 넣으면, count가 변경 될 때 마다 새롭게 함수를 할당한다.

...

// count를 초기화해주는 함수
const initCount = useCallback(() => {
  console.log(`[COUNT 변경] ${count}에서 0으로 변경되었습니다.`);
  setCount(0);
}, [count]);

...

useMemo

: 여기서 말하는 memo는 memoization을 뜻한다. 기억한다는 말이다.

동일한 값을 반환하는 함수를 계속 호출해야 하면 필요없는 렌더링을 한다고 볼 수 있다. 맨 처음 해당 값을 반환할 때 그 값을 특별한 곳(메모리)에 저장한다.. 이렇게 하면 필요할 때 마다 다시 함수를 호출해서 계산하는게 아니라 이미 저장한 값을 단순히 꺼내와서 쓸 수 있다. 보통 이러한 기법을 캐싱을 한다. 라고 표현한다.

 

사용방법

dependency Array의 값이 변경 될 때만 **반환할_함수()**가 호출된다.

그 외의 경우에는 memoization 해놨던 값을 가져오기만 한다.

// as-is
const value = 반환할_함수();

// to-be
const value = useMemo(()=> {
	return 반환할_함수()
}, [dependencyArray]);
const me = useMemo(() => {
  return {
    name: "Ted Chang",
    age: 21,
    isAlive: isAlive ? "생존" : "사망",
  };
}, [isAlive]);

~~

~~ 나중에 적기

useMemo()만 이렇게 써주면, uselessCount가 아무리 증가돼도 영향이 없게 된다.

 

주의해야 할 사항

useMemo를 남발하게 되면 별도의 메모리 확보를 너무나 많이 하게 되기 때문에 오히려 성능이 악화될 수 있다.

필요할 때만 쓰기😎

'React' 카테고리의 다른 글

axios interceptor  (0) 2023.04.29
비동기 통신 - axios(get)  (0) 2023.04.28
React Hooks - useRef  (0) 2023.04.19
React Hooks - useEffect  (0) 2023.04.19
React Component  (0) 2023.04.18

useRef

DOM 요소에 접근할 수 있도록 하는 React Hook!

✔예를 들면 화면이 렌더링 되자마자 특정 input 태그가 focusing이 돼야 하는 경우 등

 

사용방법

그리고, 변경도 가능해요!

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

function App() {
  const ref = useRef("초기값");
  console.log("ref", ref);
  
//  function App() {
//  const ref = useRef("초기값");
//  console.log("ref 1", ref);

//  ref.current = "바꾼 값";
//  console.log("ref 1", ref);

  return (
    <div>
      <p>useRef에 대한 이야기에요.</p>
    </div>
  );
}

export default App;

 

⭐이렇게 설정된 ref 값은 컴포넌트가 계속해서 렌더링 되어도 unmount 전까지 값을 유지한다!

이러한 특징 때문에 useRef는 다음 2가지 용도로 사용된다.

1. 저장공간

- state와 비슷한 역할을 해요. 다만 state는 변화가 일어나면 다시 렌더링이 일어나요. 내부 변수들은 초기화가 되죠.

- ref에 저장한 값은 렌더링을 일으키지 않아요. 즉, ref의 값 변화가 일어나도 렌더링으로 인해 내부 변수들이 초기화 되는 것을 막을 수 있죠.

- 컴포넌트가 100번 렌더링 → ref에 저장한 값은 유지돼요.

정리하면

state는 리렌더링이 꼭 필요한 값을 다룰 때 쓰면 된다.

ref는 리렌더링을 발생시키지 않는 값을 저장할 때 사용한다.

2. DOM

- 렌더링 되자마자 특정 input이 focusing 돼야 한다면 useRef를 사용하면 된다.

 

state와 ref의 차이점

state는 변경되면 렌더링이 되고, ref는 변경되면 렌더링이 안된다는걸 다시 한번 기억하기!!!

 

DOM 접근

<input /> 태그에는 ref라는 속성이 있다. 이걸 통해 우리는 해당 DOM 요소로 접근할 수 있다.

useRef 사용하여 포커싱 주기

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

function App() {
  const idRef = useRef("");

  // 렌더링이 될 때
  useEffect(() => {
    idRef.current.focus();
  }, []);

  return (
    <>
      <div>
        아이디 : <input type="text" ref={idRef} />
      </div>
      <div>
        비밀번호 : <input type="password" />
      </div>
    </>
  );
}

export default App;

아이디가 10자리 입력되면 자동으로 비밀번호 필드로 이동하는 코드

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

function App() {
  const idRef = useRef("");
  const pwRef = useRef("");

  const [id, setId] = useState("");

  const onIdChangeHandler = (event) => {
    setId(event.target.value);
  };

  // 렌더링이 될 때
  useEffect(() => {
    idRef.current.focus();
  }, []);

  // 왜 useEffect 안에 놓았을까요?
  useEffect(() => {
    if (id.length >= 10) {
      pwRef.current.focus();
    }
  }, [id]);

  return (
    <>
      <div>
        아이디 :
        <input
          type="text"
          ref={idRef}
          value={id}
          onChange={onIdChangeHandler}
        />
      </div>
      <div>
        비밀번호 : <input type="password" ref={pwRef} />
      </div>
    </>
  );
}

export default App;

비밀번호 타입이 패스워드? -> 숫자 가려줌

 

id.length ≥ 10 이 로직을 useEffect 안에 넣었는지?

아이디가 하나하나 바뀔 때마다 화면이 계속해서 바뀐다 -> state가 갱신되고 있다. -> 랜더링이 다시 계속해서 된다.

state가 바뀔 때마다 어떤 것(id가 입력된 길이가 10자리냐 아니냐)을 파악해야 한다.

[id]값이 바뀔 때마다 수행이 돼야 하기 때문에.

'React' 카테고리의 다른 글

비동기 통신 - axios(get)  (0) 2023.04.28
React Hooks - 최적화(React.memo, useCallback, useMemo)  (1) 2023.04.19
React Hooks - useEffect  (0) 2023.04.19
React Component  (0) 2023.04.18
State 불변성  (0) 2023.04.15

useEffect

리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook이다. 쉽게 말해 어떤 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶다면? 또는 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면? useEffect를 사용한다.

React에서 제공하는 훅 (기능) 이므로, import React, { useEffect } from "react";import 해서 사용합니다.

 

useEffect는 화면에 컴포넌트가 mount 또는 unmount 됐을 때 실행하고자 하는 함수를 제어하게 해주는 훅이다.

useEffect 에서 함수를 1번만 실행시키고자 할때는 의존성 배열을 빈 배열로 둔다.

 

useEffect 기초

App 컴포넌트가 화면에 렌더링될 때 useEffect 안에 있는 console.log가 실행된다. 컴포넌트가 렌더링 될 때 실행되는 것, 이게 바로 useEffect 핵심 기능이다.

// src/App.js

import React, { useEffect } from "react";

const App = () => {

  useEffect(() => {
		// 이 부분이 실행된다.
    console.log("hello useEffect");
  });

  return <div>Home</div>;
}

export default App;

 

useEffect와 리렌더링(re-rendering)

useEffect는 useEffect가 속한 컴포넌트가 화면에 렌더링 될 때 실행된다. 이런 useEffect의 특징에 의해 우리가 의도치않은 동작을 경험할수도 있다.

console.log("hello useEffect"); 가 계속 실행되서 브라우저 콘솔에 텍스트가 계속 올라오는 것을 볼 수 있다.

import React, { useEffect, useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  useEffect(() => {
    console.log("hello useEffect");
  });

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          setValue(event.target.value);
        }}
      />
    </div>
  );
}

export default App;

전체 흐름은 아래와 같다.

  1. input에 값을 입력한다.
  2. value, 즉 state가 변경된다.
  3. state가 변경되었기 때문에, App 컴포넌트가 리렌더링 된다.
  4. 리렌더링이 되었기 때문에 useEffect가 다시 실행된다.
  5. 1번 → 5번 과정이 계속 순환환다.

의존성 배열

: 이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행하는 것!

// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
	// 실행하고 싶은 함수
}, [의존성배열])

의존성 배열이 빈 배열인 경우 [ ]

아무것도 넣지 않았으니 useEffect는 처음에 딱 한번만 실행되고 그 이후로는 어떤일이 일어나도 실행이 되서는 안된다.

input에 어떤 값을 입력하더라도, 처음에 실행된 hello useEffect외에는 더 이상 실행이 되지 않는 것을 볼 수 있다. 이렇게 useEffect를 사용하는데, 어떤 함수를 컴포넌트가 렌더링 될 때 단 한번만 실행하고 싶으면 의존성 배열을 [ ] 빈 상태로 넣으면 된다.

 

의존성 배열에 값이 있는 경우 [의존성배열]

의존성 배열에 value 를 넣는다. 우리가 배운게 맞다면, value는 state이고 우리가 input을 입력할 때마다 그 값이 변하게 되니 useEffect도 계속 실행이 된다.

'React' 카테고리의 다른 글

React Hooks - 최적화(React.memo, useCallback, useMemo)  (1) 2023.04.19
React Hooks - useRef  (0) 2023.04.19
React Component  (0) 2023.04.18
State 불변성  (0) 2023.04.15
State  (0) 2023.04.15

컴포넌트를 통해 UI를 재사용이 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있다. 개념적으로 컴포넌트는 JavaScript 함수와 유사하다. “props”라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트(element:요소)를 반환한다.

 

리액트 컴포넌트(블럭, 즉 함수(html을 return하는 함수))를 표현하는 두 가지 방법

1. 함수형 컴포넌트(많이 사용)

// props라는 입력을 받음
// 화면에 어떻게 표현되는지를 기술하는 React 엘리먼츠를 반환(return)

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 훨씬 쉬운 표현을 해보면 아래와 같죠.
function App () {
	return <div>hello</div>
}

2. 클래스형 컴포넌트

 

Component 보는 방법

// 컴포넌트 밖, 내가 필요한 파일을 가져오는 영역
import React from 'react'; 
function App() {
  
	// <---- 자바스크립트 영역 ---->
	const handleClick = () => {
    alert('클릭!');
  };
  
  return (
  /* <---- HTML/JSX 영역  ---->*/
	<div
      style={{
        height: '100vh',
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
      }}
    >
      <span>이것은 내가 만든 App컴포넌트 입니다</span>
      <button onClick={handleClick}>클릭!</button>
    </div>
  );
}
   {/* 이곳에 퀴즈를 위한 html 코드를 작성해 주세요 */}
    </div>
  );
}
// 내가 만든 컴포넌트를 밖으로 내보내는 영역
export default App;

 

 

주의!

컴포넌트를 만들 때 반드시 가장 첫 글자는 대문자로 만들어야 한다.

폴더는 소문자로 시작하는 카멜케이스로 작성하고, 컴포넌트를 만드는 파일은 대문자로 시작하는 카멜케이스로 이름을 짓는다.

 

부모-자식 컴포넌트

컴포넌트 안에 컴포넌트 넣기

컴포넌트는 다른 컴포넌트를 품을 수 있다. 이때 다른 컴포넌트를 품는 컴포넌트를 부모 컴포넌트라고 부르고, 다른 컴포넌트 안에서 품어지는 컴포넌트를 자식 컴포넌트라고 부른다.

import React from "react";

function Child() {
  return <div>연결 성공</div>;
}

function Mother() {
  return <Child />;
}

function GrandFather() {
  return <Mother />;
}

function App() {
  return <GrandFather />;
}

export default App;

이렇게 코드를 작성하면 "연결 성공" 문장이 보여진다. 이 파일에서 내보내진( “내보내진” 이라는 것을 export default 라고 하기로 했다.) 컴포넌트는 App 컴포넌트 이기때문에 App 컴포넌트가 화면에 보여진다. 하지만 App 컴포넌트는 GrandFather 컴포넌트를 자식으로 삼고, GrandFather -> Mother, Mother  ->  Child

그래서 결국 자식 컴포넌트에 있는 “연결 성공" 라는 문장이 보여지게 되는 것 이다.

'React' 카테고리의 다른 글

React Hooks - useRef  (0) 2023.04.19
React Hooks - useEffect  (0) 2023.04.19
State 불변성  (0) 2023.04.15
State  (0) 2023.04.15
Props, Props Children, Props 추출  (0) 2023.04.14

: 메모리에 있는 값을 변경할 수 없는 것!

 

원시 데이터(숫자, 문자, 불리언... ) : 불변성 있음

원시데이터는 수정을 했을 때 메모리에 저장된 값 자체는 바꿀 수 없고, 새로운 메모리 저장공간에 새로운 값을 저장한다.

 

원시데이터가 아닌 것들(배열, 객체, 함수... ) : 불변성 없음

원시데이터가 아닌 데이터는 수정했을 때 기존에 저장되어 있던 메모리 저장공간의 값 자체를 바꿔버린다.

왜 리액트에서는 원시데이터가 아닌 데이터의 불변성을 지켜주는 것을 중요시할까?

: state가 변했으면 리렌더링 하는 것이고, state가 변하지 않았으면 리렌더링을 하지 않는다.

state가 변했는지 변하지 않았는지 확인하는 방법이 state의 변화 전, 후의 메모리 주소를 비교한다. 그래서 만약 리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 가하면 값은 바뀌지만 메모리주소는 변함이 없게 된다.

즉, 개발자가 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못하게 된다. 그래서 결국 마땅히 일어나야 할 리렌더링이 일어나지 않게된다.

 

리액트 불변성 지키기 예시

배열을 setState 할 때 불변성을 지켜주기 위해, 직접 수정을 가하지 않고 전개 연산자를 사용해서 기존의 값을 복사하고, 그 이후에 값을 수정하는 식으로 구현합니다.

import React, { useState } from "react";

function App() {
  const [dogs, setDogs] = useState(["진돗개"]);

  function onClickHandler() {
		// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다. 
	  // 그리고 나서 항목을 추가합니다.
    setDogs([...dogs, "한국의 토종견"]);
  }

  console.log(dogs);
  return (
    <div>
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

index.js에서 주석처리하면 아래처럼 콘솔에서 하나만 나옴

 

랜더링

: 컴포넌트가 현재 props와 state의 상태에 기초하여 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업

 

배열 또는 객체가 나온다면 스프레드 문법 or map or filter,

불변성을 지켜주는 여러가지 방법이용해서 원시데이터가 아닌 것을 처리한다 

import React, { useState } from "react";

function App() {
  const [dogs, setDogs] = useState({
    name: 'merry',
    age: 5,
  });

  return (
    <div>
      <div>{dogs.name}</div>
      <button 
       onClick={() => {
        dogs.name = 'cutemerry'
        const dogs2 = { ...dogs }
        setDogs(dogs2)
      }}>
        버튼
        </button>
    </div>
  );
}

export default App;

'React' 카테고리의 다른 글

React Hooks - useEffect  (0) 2023.04.19
React Component  (0) 2023.04.18
State  (0) 2023.04.15
Props, Props Children, Props 추출  (0) 2023.04.14
JSX(JavaScript + XML)  (0) 2023.04.14

: 컴포넌트 내부에서 바뀔 수 있는 값! (리액트 안에서 변경되어야 하는 값은 반드시 state로 선언)

왜? UI를 바꾸기 위해서

 

State를 만들기

useState() : '훅' (리액트에만 존재하는 개념이자 기능)

import React, { useState } from 'react';

function PororoFriends() {
  const [name, setName] = useState("루피"); // 이것이 state!
  return <Second PororoFriends={name} />;
}

// .. 중략

useState 훅을 사용하는 방식

const [ value, setValue ] = useState( 초기값 ) => ( ' ' )가능   // 구조분해할당 

const [name, setName] = useState("루피")

name이라는 state 만들었고, name state의 처음값(initial state : 언제든지 변할 수 있는 값이기 때문에 존재)은 “루피”

 

State 변경하기

🌞onClick

setValue(바꾸고 싶은 값)를 사용 ( 아래에서는 setName )

import React, { useState } from "react";

function Third(props) {
  return (
    <div>
      <button
        onClick={() => {   // 여기에서 setName()을 실행
          props.setName("뽀로로");    // 드디어 받은 setName을 실행
        }}
      >
        루피 이름 바꾸기
      </button>
      <div>{props.PororoFriendsName}</div>
    </div>
  );
}

function Second(props) {
  return (
    <Third PororoFriendsName={props.PororoFriendsName} setName={props.setName} /> // 받아서 다시 주고
  );
}

function First() {
  const [name, setName] = useState("루피");
  return <Second PororoFriendsName={name} setName={setName} />; // 주고
}

function App() {
  return <First />;
}

export default App;

 

 

 

setName을 통해서 바꾼 값은 어디에 저장x => 단순히 화면에서만 바뀐 값으로 다시 렌더링(새로고침 시 초기값 나옴)

 

🌞useState + onClick Event

(1) Button과 이벤트 핸들러 구현하기

onClickHandler 라는 함수를 만들고 onClick={} 에 넣어주었다. React에서는 이러한 방식으로 함수와 컴포넌트(button 태그)를 연결. 이 함수를 이벤트 핸들러 라고 표현.

import React from "react";

function App() {
  // 버튼을 눌렀을 때 하고 싶은 행동 
  function onClickHandler() {
    console.log("hello button");
  }
  return (
    <div>
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

(2) state 구현하고 이벤트 핸들러와 연결하기

state를 하나 만들고, 이 버튼을 클릭을 했을 때 state 값을 바꾼다.

이벤트 핸들러를 만들어주고 그 안에 setName 을 넣어줍니다.

이제 우리가 버튼을 누르면 setName()안에 있는 값이 “체리”이니까, state가 “사과"에서 “체리”로 바뀐다.

import React, { useState } from "react";

function App() {
  const [fruit, setFruit] = useState("사과");

  function onClickHandler() {
    setFruit("체리");
  }

  return (
    <div>
      {fruit}
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;

 

🌝 useState + onChange + Event

(1) Input과 state 구현하기

(2) 이벤트핸들러를 구현하고 state와 연결하기

이벤트 핸들러 안에서 자바스크립트의 event 객체를 꺼내 사용할 수 있다. 사용자가 입력한 input의 값은 event.target.value 로 꺼내 사용할 수 있다. 마지막으로 state인 value를 input의 attribute인 value에 넣어주면 input과 state 연결하기, 끝

import React, { useState } from "react";

const App = () => {
  const [value, setValue] = useState("");

  const onChangeHandler = (event) => {
    const inputValue = event.target.value;
    setValue(inputValue);
  };

	console.log(value) // value가 어떻게 변하는지 한번 콘솔로 볼까요?

  return (
    <div>
      <input type="text" onChange={onChangeHandler} value={value} />
    </div>
  );
};

export default App;

다시 설명하면 사용자가 input에 어떤 값을 입력하면, 그 값을 입력할 때마다, 같은 말로 onChange될 때마다 value라는 state에 setValue해서 넣어주는 것 이다.

 

import React, { useState } from "react";

const App = () => {
  const [fruit, setFruit] = useState("");

  return (
    <div>
    	과일 : {" "}
      <input 
      	value={fruit} 
     	 onChange={function (event) {
      		setFruit(event.targer.value)   
            // console.log('event', event.target.value)
      }} 
      />
     <br></br>
     {fruit}
    </div>
  );
};

export default App;

이벤트 핸들러 안에서 자바스크립트의 event 객체를 꺼내 사용할 수 있다

Event 객체 안에 있는 target 객체안에 value라는 값으로 우리가 타이핑 하는 것들이 들어온다. 매개변수로!

=> input의 값은 event.target.value

state인 value를 input의 attribute인 value에 넣어주면 input과 state 연결하기

 

사용자가 input에 어떤 값을 입력하면, 그 값을 입력할 때마다,

같은 말로 onChange될 때마다 value라는 state에 setValue해서 넣어주는 것

 

'React' 카테고리의 다른 글

React Hooks - useEffect  (0) 2023.04.19
React Component  (0) 2023.04.18
State 불변성  (0) 2023.04.15
Props, Props Children, Props 추출  (0) 2023.04.14
JSX(JavaScript + XML)  (0) 2023.04.14

+ Recent posts