AC 03/19 React

Bae Seong Jun·2024년 3월 19일

Acorn academy

목록 보기
56/70

자식컴포넌트에 dispatch 넘겨서 사용

import React, { createContext, useReducer, useState } from 'react';
import Button from './components/Button';

export const UserContext = createContext(null);

function App(props) {
  const reducer = (current_state, action) => {
    switch (action.type) {
      case "ADD":
        return [...current_state, action.xxx];
      case "DEL":
        return [current_state.filter((v, i)=>v!= action.xxx)]
      default:
        return current_state;
    }
  }


  const [item, setItem] = useState();
  const [items, dispatch] = useReducer(reducer, []);

  function handleChange(event){
    setItem(event.target.value);
  }
  console.log(items);

  return (
    <>
      <UserContext.Provider value={[dispatch, item]}>
        <div>
          <input type='text' value={item} onChange={handleChange}></input>
          <Button></Button>

        </div>
        {items}
      </UserContext.Provider>
    </>
  );
}

export default App;
import React, { useContext } from 'react';
import { UserContext } from '../App';

function Button(props) {
    const [dispatch, item] = useContext(UserContext);
    return (
        <div>
            <button onClick={()=>dispatch({type:"ADD", xxx:item})}>+</button>
            <button onClick={()=>dispatch({type:"DEL", xxx:item})}>-</button>
        </div>
    );
}

export default Button;

useEffect

라이프사이클 훅 메서드들은 클래스형 컴포넌트에서만 사용할 수 있다.
이를 대체한게 useEffect다.

콜백함수가 몇번 실행될지가 배열과 연관이 있다.
반환값이 없다.
익명함수는 바로 실행되지 않고 App이 실행되어 DOM이 랜더링된 후에 비동기로 실행된다. 이후[의존성배열]에 의해서 익명함수의 재실행 여부가 결정된다.

익명함수는 바로 실행되지 않고 App이 실행되어 DOM이 랜더링된 후에 비동기로 실행된다.

만약 [의존성배열]을 지정하면 값일 변경될 때만 익명함수가 재실행되고
지정하지 않으면 App이 다시 실행될 때마다 익명함수도 재실행된다.
만약 [] 빈 배열을 지정하면 종속값이 없기 때문에 익명함수는 단 한번만 실행된다.

strict 모드로 실행하면 2번 호출. index.js 수정.

[] 빈배열을 자주 쓸 거임.
화면 로딩시 서버에서 데이터 가져오는 용도로 많이 사용할 것임

약간 jquery의 ajax, Vue의 axios와 비슷한 역할을 수행한다.

요약: 변수, 랜더링, 최초 기준으로 정해진 함수를 실행 (DOM이 랜더링 된 후에 실행되어야하므로 App이 먼저 실행된 후에 비동기로 실행된다.)

예제

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

function App(props) {
  const [number, setNumber] = useState(0); 
  
  useEffect(()=>{
    console.log("useEffect number값이 변경됨");
  }, []);

  console.log("App 호출");
  return (
    <div>
      <h1>{number}</h1>
      <button onClick={()=>setNumber(()=>number+1)}>+</button>
      <button onClick={()=>setNumber(()=>(number<1)?number:number-1)}>-</button>
    </div>
  );
}

export default App;

useEffect를 사용한 첫 화면 로딩시 이벤트 매핑

인라인 이벤트 onClick등을 제거하고 useEffect로 구현해보기

예제

  • num안바뀌는 오류가 있음 => 찾기
import React, { useEffect, useState } from 'react';

function App(props) {
  const [num, setNum] = useState(0);
  useEffect(()=>{
    //document.getElementById("plus").addEventListener('click', plus);
    document.querySelector("#plus").addEventListener('click', plus);
    console.log("useEffect");
  },[]);
  function plus(){
    setNum(()=>num+1);
    console.log("plus");
  }
  console.log("app", num);
  return (
    <div>
      <h1>{num}</h1>
      <button id="plus">+</button>
    </div>
  );
}
export default App;

성능 최적화 기법

memo 함수

부모가 자식에게 전달하는 props가 똑같을 때, 부모가 랜더링되고 자식은 랜더링이 필요가 없어진다. 이때 memo를 통해 자식의 랜더링을 방지할 수 있다.

사용법 : 자식 컴포넌트를 메모로 묶어주면 된다.

import React, { useState } from 'react';
import Counter from './components/Counter';
import Counter2 from './components/Counter2';
import Counter3 from './components/Counter3';
import Counter4 from './components/Counter4';
function App(props) {
  const [num, setNum] = useState(0);

  function handleEvent(){
    setNum(num+1);
  }
  console.log("App");
  return (
    <div>
      <Counter></Counter>
      <Counter2 initialCount={num}></Counter2>
      <Counter3 initialCount={10}></Counter3>
      <Counter4 initialCount={10}></Counter4>

      <button onClick={handleEvent}>click</button>
      <hr></hr>
      {num}
    </div>
  );
}

export default App;
import React from 'react';
import {memo} from 'react';

const Counter4 = memo(
    ({initialCount = 0}) =>{
        console.log("count4");
        return (
            <div>
                count4값 : {initialCount}
                <br/>
            </div>
        );
    }
    
)

export default Counter4;

useMemo

useEffect와 비슷한 기능을 하며, 차이점은 동일한 입력이 들어오면 이전에 저장해둔 복잡한 연산의 결과값을 추가 연산 없이 사용하여 성능을 최적화시킬 수 있다는 점이다.
콜백함수와 배열을 인자로 가지며, 성능을 최적화시킬 수 있는 대표적인 hook 중 하나다.

여기서 입력이란 을 뜻하며, 배열의 요소값이 업데이트될 때만 콜백함수를 다시 호출한다.
만약 빈 배열을 지정하면 맨 처음 컴포넌트가 마운드되었을 때만 값을 계산하고 이후에는 항상 memoization된 값을 반환한다.
결과적으로 콜백함수가 리턴시킨 결과값을 반환한다.

https://velog.io/@jinyoung985/React-useMemo%EB%9E%80
이런 경우에 사용할 수 있다:
객체를 담은 변수를 useEffect의 배열에 담았다.
그리고 다른 useState로 변경한 state로 인해 App이 랜더링되었다.
그러면 객체의 주소값이 변경되고 useEffect가 실행되게된다.
...
...

unction App() {
  const [number, setNumber] = useState(1);
  const [isKorea, setIsKorea] = useState(true);

  // 1번 location
  const location = {
    country: isKorea ? "한국" : "일본"
  };

  // 2번 location
  // const location = useMemo(() => {
  //   return {
  //     country: isKorea ? '한국' : '일본'
  //   }
  // }, [isKorea])

  useEffect(() => {
    console.log("useEffect 호출!");
  }, [location]);

  return (
    <header className="App-header">
      <h2>하루에 몇 끼 먹어요?</h2>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />
      <hr />

      <h2>내가 있는 나라는?</h2>
      <p>나라: {location.country}</p>
      <button onClick={() => setIsKorea(!isKorea)}>Update</button>
    </header>
  );
}

export default App;

보충학습
https://velog.io/@goyou123/React-Hooks-%EC%B4%9D%EC%A0%95%EB%A6%AC


http 요청

개인적 설치 : 크롬 json viewer 확장프로그램 사용

더미데이터 제공하는 서버 사이트 : https://reqres.in/
아래에 보면 여러가지 api요청주소와 반환받는 데이터를 보여준다.
이를 이용하여 프론트엔드에서 api 테스트를 수월히 진행할 수 있다.

실습 - 1

// fetch로 비동기 통신
// fetch는 Promise 객체를 반환함
// fetch 비동기 요청 :promise 객체 리턴 => await 처리
// await 사용함수는 전체를 async처리 해주어야함
// await을 사용하기 위해서는 함수에 async를 추가해줘야함

export async function fetchUserList(){
    const response = await fetch("https://reqres.in/api/users?page=2");

    const resData = await response.json();
    console.log(resData);
    return resData.data;
}
import React, { useEffect, useState } from 'react';
import UserList from './components/UserList';
import { fetchUserList } from './http';

function App(props) {
  const [usersList, setUsersList] = useState([]);
  useEffect(()=>{
  	// await을 사용하기 위해서는 함수에 async를 추가해줘야함
    async function xxx(){
      //비동기 함수의 구현
      const usersList = await fetchUserList();
      setUsersList(usersList);
    }
    xxx();
  }, [])
  return (
    <div>
      <h1>User목록</h1>
      <UserList users={usersList}></UserList>
    </div>
  );
}

export default App;
import React from 'react';

function UserList(props) {
    const {users} = props;
    return (
        <div>
            <table border={1}>
                <thead>
                    <tr><th>아이디</th><th>이름</th><th>사진</th></tr>
                </thead>
                <tbody>
                {users.map((user,idx)=>{
                    return (
                        <tr key={idx}>
                            <td>{user.id}</td>
                            <td>{user.first_name}</td>
                            <td><img src={user.avatar} width="50" height="50"/></td>
                        </tr>
                    )
                })}
                </tbody>
            </table>
        </div>
    );
}

export default UserList;

어려운 실습 2 -

이거 하기 전에 fetch와 promise 객체에 대해서 알아야 사실 정확한 이해가 가능할 것 같다.
자바스크립트 fetch 간편 정보 : https://www.doyeon0430.com/engineer/django/54/

  1. http.js에서 비동기통신 함수를 생성 (https://reqres.in/ 로 데이터 요청)
  • await fetch 이용 => 반환값은 promise 객체 (아래와 같은 값들을 갖고 있다.)

    ok로 성공/실패 판단
    => 실패라면 throw new Error("오류메시지") -> Error.js로 결과화면 표현
    => 성공이라면 아래와 같은 방식으로 데이터를 파싱해서 리턴 (왜 이렇게 하는지는 promise 객체에 대해 추가적으로 학습해야할듯하다.)
    const resData = await response.json();
    return resData.data;
  1. App.js에서 http.js의 함수를 사용하여 받아온 리스트 데이터를 표의 형태로 userList.js에 출력
  2. 비동기 통신이 완료되기 전까지 로딩화면을 userList에 출력되게 수정

http.js

// fetch로 비동기 통신
// fetch는 Promise 객체를 반환함
// fetch 비동기 요청 :promise 객체 리턴 => await 처리
// await  사용함수는 전체를 async처리 해주어야함

export async function fetchUserList(){
    const response = await fetch("https://reqres.in/api/users?page=2", {
        method: "get"
    });
    console.log("response 객체 정체: ", response);
    if(!response.ok){
        console.log("fetchUserList.Error");
        throw new Error("UserList 요청 예외")
    }
    const resData = await response.json();
    console.log(resData);
    return resData.data;
}

App.js

import React, { useEffect, useState } from 'react';
import UserList from './components/UserList';
import { fetchUserList } from './http';
import Error from './components/Error';

function App(props) {
  const [usersList, setUsersList] = useState([]);
  const [isFetching, setIsFetching] = useState(false);
  const [error, setError] = useState();
  useEffect(()=>{
    async function xxx(){
      setIsFetching(true); //fetching 가져오는 중
      //비동기 함수의 구현
      try {
        const usersList = await fetchUserList()
        setUsersList(usersList);
      } catch (error) {
        console.log("Error: ", error.message);
        setError({message: error.message});
      } finally {
        setIsFetching(false);
      }
      
    }
    xxx();
  }, [])
  return (
    <div>
      <h1>User목록</h1>
      {error && (
        <Error title="User목록 : 예외발생됨" message={error.message}></Error>
      )}
      {!error && (
        <UserList
          isLoading={isFetching}
          users={usersList}
          loadingText="Fetching your users...."
          fallbackText="No users available"
        ></UserList>
      )}
      {/* <h1>User목록</h1>
      {(isFetching)?
      <p style={{color:"red"}}>fetching loading...</p>
      :
      (error)?<Error title="User목록 : 예외발생됨" message={error.message}></Error>:<UserList users={usersList}></UserList>
      } */}

    </div>
  );
}

export default App;

userList.js

import React from 'react';

function UserList(props) {
    console.log(props);
    const {isLoading, users, loadingText, fallbackText} = props;
    return (
        <div>
            {isLoading && <p style={{color: "red"}}></p>}
            {!isLoading && users.length ==0 && (
                <p style={{color:"blue"}}>{fallbackText}</p>
            )}
            {!isLoading && users.length>0&&(
                <table border={1}>
                    <thead>
                        <tr><th>아이디</th><th>이름</th><th>사진</th></tr>
                    </thead>
                    <tbody>
                    {users.map((user,idx)=>{
                        return (
                            <tr key={idx}>
                                <td>{user.id}</td>
                                <td>{user.first_name}</td>
                                <td><img src={user.avatar} width="50" height="50"/></td>
                            </tr>
                        )
                    })}
                    </tbody>
                </table>
            )}
            
        </div>
    );
}

export default UserList;

Error.js

import React from 'react';

function Error(props) {
    console.log("inError: ", props);
    const {title, message} = props;
    return (
        <div>
            <h2>{title}</h2>
            <p>{message}</p>
        </div>
    );
}

export default Error;
profile
코딩 프로?

0개의 댓글