안녕하세요. 유튜브에 있는 "리액트 프로젝트 영상"을 보면서 코딩을 하던 중 정리도 하고, 정보를 나누고 싶어 글을 남기게 되었습니다.
여러분들은 리액트의 기본 동작원리에 대해서 잘 알고 계신가요?
잘 정리된 글 하나를 소개해드립니다!
⭐ juno 님이 정리해주신 리액트의 동작원리
일단 위의 글을 읽고 오시는 것을 추천드립니다‼️(강추)
자 읽고 오셨겠죠?🫡
React는 다음과 같은 경우에 리렌더링이 일어납니다.
1. Props가 변경되었을 때
2. State가 변경되었을 때
3. forceUpdate() 를 실행하였을 때.
4. 부모 컴포넌트가 렌더링되었을 때
라고 주노님께서 정리를 잘 해주셨습니다!! 이 네 가지 경우를 잘 기억해주세요.
import React from 'react';
const Card = ({ pokemon, loading, infoPokemon }) => {
console.log(pokemon);
return (
<>
{loading ? (
<h1>Loading...</h1>
) : (
pokemon.map((item) => {
return (
<>
<div
className='card'
key={item.id}
onClick={() => infoPokemon(item)}
>
<h2>{item.id}</h2>
<img src={item.sprites.front_default} alt='' />
<h2>{item.name}</h2>
</div>
</>
);
})
)}
</>
);
};
export default Card;
import React from 'react';
const Pokeinfo = ({ data }) => {
console.log(data);
return (
<>
{!data ? (
''
) : (
<>
<h1>{data.name}</h1>
<img
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/${data.id}.svg`}
alt=''
/>
<div className='abilities'>
{data.abilities.map((poke) => {
return (
<>
<div className='group'>
<h2>{poke.ability.name}</h2>
</div>
</>
);
})}
</div>
<div className='base-stat'>
{data.stats.map((poke) => {
return (
<>
<h3>
{poke.stat.name}:{poke.base_stat}
</h3>
</>
);
})}
</div>
</>
)}
</>
);
};
export default Pokeinfo;
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Card from './Card';
import Pokeinfo from './Pokeinfo';
const Main = () => {
const [pokeData, setPokeData] = useState([]);
const [loading, setLoading] = useState(true);
const [url, setUrl] = useState('https://pokeapi.co/api/v2/pokemon/');
const [nextUrl, setNextUrl] = useState();
const [prevUrl, setPrevUrl] = useState();
const [pokeDex, setPokeDex] = useState();
const pokeFunc = async () => {
setLoading(true);
const res = await axios.get(url);
setNextUrl(res.data.next);
setPrevUrl(res.data.previous);
getPokemon(res.data.results);
setLoading(false);
// console.log(pokeData);
};
const getPokemon = async (res) => {
res.map(async (item) => {
const result = await axios.get(item.url);
setPokeData((state) => {
state = [...state, result.data];
state.sort((a, b) => a.id - b.id);
return state;
});
});
};
useEffect(() => {
pokeFunc();
}, [url]);
return (
<>
<div className='container'>
<div className='left-content'>
<Card
pokemon={pokeData}
loading={loading}
infoPokemon={(poke) => setPokeDex(poke)}
/>
<div className='btn-group'>
{prevUrl && (
<button
onClick={() => {
setPokeData([]);
setUrl(prevUrl);
}}
>
Previous
</button>
)}
<button
onClick={() => {
setPokeData([]);
setUrl(nextUrl);
}}
>
Next
</button>
</div>
</div>
<div className='right-content'>
<Pokeinfo data={pokeDex} />
</div>
</div>
</>
);
};
export default Main;
자 정말 복잡하고 어지러우시죠..? 저도 그렇습니다만 천천히 얘기해보자구요.🫡
아 ! 잠깐 컴포넌트가 무엇일까요❓❗
- 여러 개의 프로그램 함수들을 모아 하나의 특정한 기능을 수행할 수 있도록 구성한 작은 기능적 단위
이해하기 쉽게 하나의 기능을 전문적으로 하는 코드 뭉치라고 생각하세요!
여기서 Main컴포넌트는 전체적인 코드를 불러오고 배치해주는 역할을 하고 있어요! 마치 전장에서 지휘하는 지휘관처럼 말이죠!
const [pokeData, setPokeData] = useState([]);
const [loading, setLoading] = useState(true);
const [url, setUrl] = useState('https://pokeapi.co/api/v2/pokemon/');
const [nextUrl, setNextUrl] = useState();
const [prevUrl, setPrevUrl] = useState();
const [pokeDex, setPokeDex] = useState();
const pokeFunc = async () => {
setLoading(true); // <--- (4)⭐
const res = await axios.get(url);
setNextUrl(res.data.next);
setPrevUrl(res.data.previous);
getPokemon(res.data.results);
setLoading(false);
// console.log(pokeData);
};
const getPokemon = async (res) => {
res.map(async (item) => {
const result = await axios.get(item.url);
setPokeData((state) => { // <-- (3)⭐
state = [...state, result.data];
state.sort((a, b) => a.id - b.id);
return state;
});
});
};
간단하게 말씀드리면 set...함수에 값을 넣으면 set...함수 옆에 있는(loading, url, nextUrl, prevUrl..)등등의 값이 변하게 됩니다. 즉 state가 변경된 겁니다.
아까 말씀 드렸던 리랜더링이 발생하는 경우중에 2번 state가 변경되었을 때를 기억하시겠죠? 이 경우가 2번 케이스에 해당합니다.
비동기 pokeFunc안에서 state가 바뀌게 됩니다. 즉 리렌더링이 발생하게 됩니다.
pokeFunc()이 실행되면 getPokemon()함수가 실행됩니다. 인자로는 api의 객체를 가진 배열을 전달합니다(res.data.results). 배열 안에는 20개의 포켓몬 객체 정보가 있습니다.
자 이건 별로 중요하지 않고, getPokemon()안에 setPokeData(3)참조☝️ 있습니다. 아까도 말씀드렸듯이 state의 변화가 일어났습니다!
return (
<>
<div className='container'>
<div className='left-content'>
<Card
pokemon={pokeData} // <--- 이 부분⭐
loading={loading}
infoPokemon={(poke) => setPokeDex(poke)}
/>
//... 생략
변화된 pokeData를 Card.jsx의 prop로 전달합니다. 잘 보시면 setLoading함수에 의해 loading은 true인 상태이고, (4) 참조☝️
const Card = ({ pokemon, loading, infoPokemon }) => {
console.log(pokemon);
return (
<>
{loading ? (
<h1>Loading...</h1>
) : (
pokemon.map((item) => {
return (
<>
//... 생략
loading이 true 상태일땐 변화된 pokeData가 보여지지 않습니다.
⭐ pokeFunc()안에 있는 getPokemon()이 끝나고 나서 setLoading(false);가 실행될 때(state가 변화될 때) 변화된 부분(pokeData, loading)을 화면에 빠르게 랜더링 해줍니다.
주노님의 realDOM 과 virtualDOM의 diffing알고리즘 부분을 참조하세용.
Card.jsx의 props중 pokemon은 방금 전 main.jsx에서 변화 된 pokeData의 값입니다. 즉 20개의 키를 가진 array를 가져오는 것이지요.
자 여기까지 이해하셨으면 제가 작성한 예제코드를 다 이해하실 수 있을거라고 생각됩니다.
정답🔥
1. onClick이 발생하면 infoPokemon의 콜백함수가 실행된다.
2. item은 cliked당한 해당 객체를 전달한다.
3. callback에 의해 pokeDex 즉 state가 변경이 된다.
4. Pokeinfo.jsx에게 바뀐 props를 전달한다.
5. 브라우저에 빠르게 표시해준다.
잘못된 정보가 있다면 편하게 말씀해주세요! 저도 배울 수 있는 기회라고 생각합니다. 언제든 환영🤗
feat. velog 첫 글인데 네이버보다 UI가 너무
맘에 드는거 실화입니까!?