애플코딩 - 리액트 강의 요약

김민재·2022년 10월 10일
0

Gotcha React!

목록 보기
4/4
post-thumbnail

컴포넌트 만드는 방법

  1. function 만들기
  2. return()안에 html 담기
  3. <함수명></함수명>쓰기

컴포넌트 만들면 좋은 경우

  1. 반복적인 html 축약 시
  2. 큰 페이지들
  3. 자주 변경되는 것들
    단, 컴포넌트의 단점은 state 가져다 쓸 때 문제가 생김

동적 UI 만드는 step

  1. html css로 미리 디자인 완성
  2. UI 현재 상태를 state로 저장
  3. state에 따라 UI 어떻게 보일지 작성

Q. 제목 클릭 시 모달창 띄우기
A. 클릭 시 state만 조절


비슷한 html 반복 생성 시

map() 쓰기
1. 왼족 array 자료만큼 내부 코드 실행
2. return 오른쪽에 있는걸 arr 담아줌
3. 유용한 파리머터 2개 사용

부모 자식 컴포넌트 state 전송 - props 문법

  1. <자식 컴포넌트 작명 {state이름}/>
  2. props 파라미터 등록 후 props.작명 사용

props 전송은 부모에서 자식만 가능

  • state 자식에 만들면 부모 -> 자식 전송 필요없음
  • state가 여러 컴포넌트에서 필요하면 상위에 만들어놔야함
    따라서 state만드는 곳은 state 사용하는 최상위 컴포넌트

컴포넌트 라이프 사이클

컴포넌트는
1. 생성이 될 수도 있고 (전문용어로 mount)
2. 재렌더링이 될 수도 있고 (전문용어로 update)
3. 삭제가 될 수도 있습니다. (전문용어로 unmount)

라이플 사이클을 알아야하는 이유는 ?

컴포넌트 라이프 사이클 중간중간에 간섭할 수 있기 때문
즉, 중간중간에 코드 실행이 가능하다.


useEffect 쓰는 진짜이유

  • useEffect 안에 적은 코드는 html 렌더링 이후에 동작
    즉, html을 다 그린 뒤에 실행된다.
  • 코드의 실행 시점을 조절할 수 있어 조금이라도 html 렌더링이 빠른 사이트를 원하면 쓸데없는 것들은 useEffect 안에 넣는게 좋다.
  • 컴포넌트의 핵심 기능은 html 렌더링
  • 그 외의 쓸데없는 기능들 :오래걸리는 반복연산, 서버에서 데이터가져오는 작업, 타이머다는 것 등
    이런건 useEffect 안에 많이 적습니다.

useEffect 실행조건 주기

useEffect(()=>{ 실행할코드 }, [state])

  • [ ]에 있는 변수나 state 가 변할 때만 useEffect 안의 코드를 실행
    (참고) [ ] 안에 state 여러개 넣을 수 있음
useEffect(()=>{ 실행할코드 }, [])
  • 아무것도 안넣으면 컴포넌트 mount시 (로드시) 1회 실행하고 영영 실행안됌
useEffect(()=>{ 실행할코드 return ()=>{} })
  • useEffect 동작하기 전에 특정코드를 실행하고 싶으면 return ()=>{} 안에 작성

(참고1) clean up function에는 타이머제거, socket 연결요청제거, ajax요청 중단 이런 코드를 많이 작성
(참고2) clean up function은 mount시엔 실행안됌
그러나 컴포넌트 unmount 시에는 clean up function 안에 있던게 1회 실행 됌

useEffect 실행조건 정리

useEffect(()=>{ 실행할코드 })
1. 이러면 재렌더링마다 코드를 실행가능합니다.

useEffect(()=>{ 실행할코드 }, [])
2. 이러면 컴포넌트 mount시 (로드시) 1회만 실행가능합니다.

useEffect(()=>{ 
  return ()=>{
    실행할코드
  }
})
  1. 이러면 useEffect 안의 코드 실행 전에 항상 실행됩니다.
useEffect(()=>{ 
  return ()=>{
    실행할코드
  }
}, [])
  1. 이러면 컴포넌트 unmount시 1회 실행됩니다.
useEffect(()=>{ 
  실행할코드
}, [state1])
  1. 이러면 state1이 변경될 때만 실행됩니다.

리액트 18버전 이상 automatic batch 기능
  • state 변경함수들이 연달아서 여러개 처리되어야한다면 state 변경함수를 다 처리하고 마지막에 한 번만 재렌더링
  • 찾아보면 setTimeout 말고 flushSync() 이런거 써도 될 것 같기도 합니다. automatic batching을 막아줌

state 전달 다른 방법

Q. 최상단 부모에 있던 state를 자식의 자식 컴포넌트에서 사용하고 싶어지면 어떻게하는가 ?
A. 부모에 -> 자식 -> 자식의 자식 props를 2번 전송
A2. Context API 문법을 쓰기
A3. Redux 같은 외부 라이브러리


Context API 문법

  • Context API 문법으로 props 없이 state 공유하기
  1. createContext() 함수를 가져와서 context를 하나 만듦
  • context를 쉽게 비유해서 설명하자면 state 보관함
  1. createContext()함수로 만든 컴포터는로 원하는 곳 감싸고 공유를 원하는 state를 value 안에 다 적기
  2. 만들어둔 Context를 import 해옵니다.
  3. useContext() 안에 넣으면 그 자리에 공유했던 state가 전부 남음

Context API 단점

  1. state 변경시 쓸데없는 컴포넌트까지 전부 재렌더링
  2. useContext() 를 쓰고 있는 컴포넌트는 나중에 다른 파일에서 재사용할 때 Context를 import 하는게 귀찮아질 수 있습니다.
    그래서 이것 보다는 redux 같은 외부라이브러리 사용

Redux 왜 쓰는가?

  • state를 Redux store에 보관가능하여 모든 컴포넌트가 거기 있던 state를 직접 꺼내쓸 수 있음
  • 즉, props 없어도 편리하게 state 공유가 가능

Redux 사용 법

  • Redux는 props 없이 state를 공유할 수 있게 도와주는 라이브러리

    Redux를 쓰면 js 파일 하나에 state들을 보관해 모든 컴포넌트가 직접 꺼내쓰기에 props 전송이 필요없어짐
  1. Redux 셋팅
import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
  reducer: { }
}) 
  • store.js 파일을 만들어서 위 코드를 적는데 이는 state들을 보관하는 파일
  1. index.js 파일가서 Provider 라는 컴포넌트와 위에 작성한 store.js을 import
import { Provider } from "react-redux";
import store from './store.js'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
  </React.StrictMode>
); 
  • 밑에 <Provider store={import해온거}>이걸로 <App/> 을 감싸기 <App/>과 그 모든 자식컴포넌트들은 store.js에 있던 state를 맘대로 꺼내쓸 수 있음

Redux store에 state 보관하는 법

  • store.js 파일 열어 다음과 같이 코드를 짜면 state 하나 만들 수 있음
  1. createSlice( ) 로 state 만들기
  2. configureStore( ) 안에 등록
  import { configureStore, createSlice } from '@reduxjs/toolkit'
let user = createSlice({
  name : 'user',
  initialState : 'kim'
})
export default configureStore({
  reducer: {
    user : user.reducer
  }
}) 
  1. createSlice( ) 상단에서 import

    { name : 'state이름', initialState : 'state값' } 넣으면 state 하나 생성
    (createSlice( ) 는 useState( ) 와 용도가 비슷)

  2. state 등록은 configureStore( ) 안에 등록

    { 작명 : createSlice만든거.reducer } 이렇게 등록한 state는 모든 컴포넌트가 자유롭게 사용가능


Redux store에 있던 state 가져다쓰는 법

import { useSelector } from "react-redux"
function Cart(){
  let a = useSelector((state) => { return state } )
  console.log(a)
  return (생략)
}
  • 아무 컴포넌트에서 useSelector((state) => { return state } ) 쓰면 store에 있던 모든 state가 그 자리에 나음

store의 state 변경법

  1. state 변경해주는 함수 만들기
  let user = createSlice({
  name : 'user',
  initialState : 'kim',
  reducers : {
    changeName(state){
      return 'john ' + state
    }
  }
}) 

slice 안에 reducers : { } 열고 거기 안에 함수 만들기

  • 함수 작명
  • 파라미터 하나 작명하면 그건 기존 state
  • return 우측에 새로운 state 입력하면 그걸로 기존 state를 갈아치움
  1. 다른 곳에서 쓰기좋게 export
export let { changeName } = user.actions 
  • 이런 코드 store.js 밑에 추가
  • slice이름.actions 라고 적으면 state 변경함수가 전부 그 자리에 출력
    해 변수에 저장했다가 export
  1. 원할 때 import 해서 사용하는데 dispatch() 로 감싸서 쓰기
import { useDispatch, useSelector } from "react-redux"
import { changeName } from "./../store.js"
<button onClick={()=>{
  dispatch(changeName())
}}>버튼임</button> 
  • store.js에서 원하는 state변경함수 가져오고
  • useDispatch 라는 것도 라이브러리에서 가져오기
  • 그리고 dispatch( state변경함수() ) 이렇게 감싸서 실행하면 state 변경

  • 컴포넌트 100개에서 직접 'kim' 이라는 state 변경하다가 갑자기 'kim'이 123이 되어버리는 버그가 발생하면 찾기 찾으려고 컴포넌트 100개를 다 뒤져야함

  • state 수정함수를 store.js에 미리 만들어두고 컴포넌트는 그거 실행해달라고 부탁만 하는 식으로 코드를 짜면 'kim'이 123이 되어버리는 버그가 발생했을 때 찾기 수월
  • 원인은 무조건 store.js에 있으니 (수정함수를 잘 만들어놨다면)

Redux - state가 object/array일 경우 변경하는 법

  1. redux state가 array/object인 경우 변경시
let user = createSlice({
  name : 'user',
  initialState : {name : 'kim', age : 20},
  reducers : {
    changeName(state){
      state.name = 'park'
    }
  }
})

state를 직접 수정해도 변경 잘 잘 변경되는 이유는 Immer.js 라이브러리가 state 사본을 하나 더 생성해준 덕분인데 Redux 설치하면 함께 쓸 수 있어서

  • array/object 자료의 경우 state변경은 state를 직접 수정
    (참고) 그래서 state 만들 때 문자나 숫자하나만 필요해도 redux에선 일부러 object 아니면 array에 담는 경우도 있음
  1. state 변경함수가 여러개 필요하면
  • state 변경함수에도 파라미터문법 사용가능
let user = createSlice({
  name : 'user',
  initialState : {name : 'kim', age : 20},
  reducers : {
    increase(state, a){
      state.age += a.payload
    }
  }
})
  • state변경함수의 둘째 파라미터를 작명하면 파라미터입력을 해서 함수사용이 가능
  • 파라미터자리에 넣은 자료들은 a.payload 하면 사용 가능
    (참고) 파라미터는 action 식으로 작명 / action.type 하면 state 변경함수 이름 / action.payload 하면 파라미터

local Storage

  • 사이트마다 5MB 정도의 문자 데이터를 저장 가능
  • object 자료랑 비슷하게 key/value 형태로 저장
  • local Storage는 유저가 브라우저 청소를 하지 않는 이상 영구적으로 남아있음
  • Session Storage도 똑같은데 브라우저 끄면 삭제되는 휘발성 가짐
  • 차례로 추가, 읽기, 삭제 문법입니다.
localStorage.setItem('데이터이름', '데이터');
localStorage.getItem('데이터이름');
localStorage.removeItem('데이터이름')

localStorage에 array/object 자료를 저장하려면?

localStorage.setItem('obj', JSON.stringify({name:'kim'}) );
  • JSON.stringify() 라는 함수에 array/object를 집어넣으면 그 자리에 JSON으로 변환된걸 남겨줌
var a = localStorage.getItem('obj');
var b = JSON.parse(a)

JSON -> array/object 변환하고 싶으면 JSON.parse()를 쓰기

localStorage에 state를 자동저장

  • redux-persist 라이브러리 설치해서 쓰면 redux store 안에 있는 state를 자동으로 localStorage에 저장
  • state 변경될 때마다 그에 맞게 localStorage 업데이트도 알아서 해주지만 셋팅문법 복잡하고 귀찮

react-query 라이브러리

ajax 요청 시 추가 기능들

  • 몇초마다 자동으로 데이터 다시 가져오기
  • 요청실패시 몇초 간격으로 재시도
  • 다음 페이지 미리가져오기
  • ajax 성공/실패시 각각 다른 html을 보여주기
    가령 SNS, 코인거래소같은 실시간 데이터를 보여줘야하는 사이트 시 유용
  • 라이브러리 이름이 react-query에서 @tanstack/react-query로 바뀜
npm install @tanstack/react-query 
  1. 설치
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query' 
  1. import 해서 사용2
import { QueryClient, QueryClientProvider } from "react-query"  //1번
const queryClient = new QueryClient()   //2번
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <QueryClientProvider client={queryClient}>  //3번
    <Provider store={store}>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
  </QueryClientProvider>
); 
useQuery(['작명'], 
  1. useQuery쓸 때 '작명' 말고 ['작명']

react-query 라이브러리 장점

1. ajax 요청 성공/실패/로딩중 상태를 쉽게 파악

result라는 변수에 ajax 현재 상태가 알아서 저장

  • ajax요청이 로딩중일 땐 result.isLoading 이 true
  • ajax요청이 실패시엔 result.error 가 true.
  • ajax요청이 성공시엔 result.data 안에 데이터가 들어옴

2. 틈만나면 알아서 ajax 재요청해줍니다.

  • 페이지 체류하고나서 일정시간이 경과하거나 다른 창으로 갔다가 다시 페이지로 돌아오거나 여러 경우에 알아서 ajax 요청을 다시 해줌
  • 재요청 끄는 법, 재요청간격 조절 가능
    장점3. 실패시 재시도 알아서 해줌

3. 실패시 재시도 알아서 해줌

  • 잠깐 인터넷이 끊겼거나 서버가 죽었을 때 등 ajax 요청이 실패했을 때는 4, 5번 재시도를 알아서 해줌

4. ajax로 가져온 결과는 state 공유 필요없음

  • props 전송 필요어지는데 필요한 컴포넌트에다가 ajax 요청하는 코드 똑같이 또 적어서 가져올 수 있음

react-query는 스마트하기 때문에 ajax 요청이 2개나 있으면 1개만 날려주고

5. 캐싱기능

  • 이미 같은 ajax 요청을 한 적이 있으면 그걸 가져와서 씀

react-query가 주장하는 장점

  • server-state (DB 데이터)를 프론트엔드에서 실시간 동기화해주는걸 도와줌
  • 근데 ajax 요청을 몇초마다 계속 날려서 가져오는 방식이라 좀 비효율적일 수도 있습니다.
  • 실시간으로 서버에서 데이터를 자주 보내려면 웹소켓이나 Server-sent events 같은 가벼운 방식들도 있음
  • react-query는 ajax 관련 기능개발 편하게 할 수 있는데에 의의가 더 있음

성능개선 1 : 개발자도구 & lazy import - React Developer Tools

React Developer Tools 설치

Components 탭

  • 개발중인 리액트사이트를 컴포넌트로 미리볼 수 있음

    -왼쪽 컴포넌트구조 파악이 가능
  • 왼쪽상단 아이콘눌러서 컴포넌트 찍으면 거기 있는 state, props 이런거 조회 및 수정 가능

Profiler 탭

  • 성능평가 가능
  • Profiler 탭 들어가서 녹화버튼 누르고 페이지 이동이나 버튼조작을 해보고 녹화를 끝내면
    방금 렌더링된 모든 컴포넌트의 렌더링시간을 측정

Redux Developer Tools

  • Redux store에 있던 state를 전부 확인가능
  • dispatch 날릴 때 마다 뭐가 어떻게 바뀌었는지 로그를 작성해줘 store 복잡질 시 유용

lazy import

  • 리액트로 코드를 빌드하려고 npm run build 하면 모든 것들을 html, css, js 파일로 변환
  • 그러나 리액트는 SPA(Single Page Application)이므로 발행하면 html, js파일 하나에 모든 코드 넣어 생성하기에 파일사이즈가 큼
    유저 메인 페이지 접속 시 html, css, 큰 js파일 다운
  • 그래서 리액트 사이트들은 첫 페이지 로딩속도가 매우 느릴 수 있음

lazy import

  • 하나의 큰 자바스크립트 파일을 잘게 분할하는 방법은 import 문법을 약간 고치기
import Detail from './routes/Detail.js'
import Cart from './routes/Cart.js'
import {lazy, Suspense, useEffect, useState} from 'react'

const Detail = lazy( () => import('./routes/Detail.js') )
const Cart = lazy( () => import('./routes/Cart.js') )

- lazy 문법으로도 똑같이 import가 가능한데 "Detail 컴포넌트가 필요해지면 import 해주세요" 라는 뜻 
- 그리고 이렇게 해놓으면 Detail 컴포넌트 내용을 다른 js 파일로 쪼개주기에 첫 페이지 로딩속도를 향상
```js
<Suspense fallback={ <div>로딩중임</div> }>
  <Detail shoes={shoes} />
</Suspense>
  • lazy 사용하면 당연히 해 컴포넌트 로드까지 지연시간이 발생할 수 있는데
  1. Suspense를 react에서 import 하여
  2. 해당 컴포넌트를 감싸면 해당 컴포넌트가 로딩중일 때 대신 보여줄 html 작성도 가능
  • <Suspense> 이걸로 <Routes> 전부 감싸도 가능

성능개선 2 : 재렌더링 막기 - memo

  • 컴포넌트가 재렌더링되면 거기 안에 있는 자식컴포넌트는 항상 함께 재렌더링 되어 자식컴포넌트가 렌더링시간이 오래 걸리는 무거운 경우가 있음

memo()로 컴포넌트 불필요한 재렌더링 막기

  1. memo를 'react' 라이브러리로부터 import
  2. 원하는 컴포넌트 정의부분을 감싸기
    단, 컴포넌트 let 컴포넌트명 = function( ){ } 식으로 만들어야 감쌀 수 있음

memo() 원리

  • props가 변경 될 때만 재렌더링 해는데 memo로 감싼 컴포넌트는 헛된 재렌더링을 안시키려고 기존 props와 바뀐 props를 비교하는 연산이 추가로 진행
  • 만약 props가 크고 복잡하면 비교 연산 자체로 인해 손해를 볼 수 있으니 꼭 필요한 곳에만 사용

성능개선 2 : 재렌더링 막기 - useMemo

  • useMemo라는 문법은 useEffect와 비슷한 용도로 컴포넌트 로드시 1회만 실행하고 싶은 코드가 있으면 거기에 담아준다.
    let res = 복잡한함수(){ }
    useMemo(() => {
        return 함수()
    }, [state])
    // 컴포넌트 렌터링 시 1회만 실행

-useEffect와 useMemo차이는 ? HTML 보여지는 return이 되면 그 때 실행되지만 useMemo는 렌더링 될때 실행된다 즉 실행 시점의 차이가 있다.


성능개선 3 : useTransition, useDeferredValue

리액트18버전 이후부터 렌더링 성능이 저하되는 컴포넌트에서 쓸 수 있는 혁신적인 기능 추가

  • useTransition 이건데 이걸로 오래걸리는 부분을 감싸면 렌더링시 버벅이지 않게 해줍니다.
    실은 코드 실행시점만 조절해주는 식임

일관된 batching

automatic batching 기능

setCount(1) 
setName(2) 
setValue(3)   //여기서 1번만 재렌더링됨
  • state변경함수를 연달아서 3개 사용하면 재렌더링도 원래 3번 되어야하지만 재렌더링을 마지막에 1회만 처리
  • 일종의 쓸데없는 재렌더링 방지기능을 batching이라 함
  • 만약 batching 되는게 싫고 state변경함수 실행마다 재렌더링시키고 싶다면 flushSync라는 함수를 사용

useTransition

렌더링시간이 매우 오래걸리는 컴포넌트 개선방법

  • useTransition() 쓰면 그 자리에 [변수, 함수]가 남음
  • 그 중 우측에 있는 startTransition() 함수로 state변경함수 같은걸 묶으면 그걸 다른 코드들보다 나중에 처리
import {useState, useTransition} from 'react'

let a = new Array(10000).fill(0)

function App(){
  let [name, setName] = useState('')
  let [isPending, startTransition] = useTransition()
  
  return (
    <div>
      <input onChange={ (e)=>{ 
        startTransition(()=>{
          setName(e.target.value) 
        })
      }}/>

      {
        a.map(()=>{
          return <div>{name}</div>
        })
      }
    </div>
  )
}
  • useTransition 동작원리
    브라우저는 기본적으로 single Tread이므로 아래와 같안 적업을 여러개 하면 버벅인다.

    startTransition으로 함수를 감싸주면 물론 특정코드의 실행시점을 뒤로 옮겨줘 (실행 시점을 조절)하여 성능 향상을 일으킨다.

isPending

  • startTransition() 으로 감싼 코드가 처리중일 때 true로 변하는 변수
{
  isPending ? "로딩중기다리셈" :
  a.map(()=>{
    return <div>{name}</div>
  })
} 

useDeferredValue 이것도 비슷함

-startTransition()와 용도가 똑같은데 state 아니면 변수하나를 집어넣을 수 있어서 변수에 변동사항이 생기면 그것을 늦게 처리

import {useState, useTransition, useDeferredValue} from 'react'
let a = new Array(10000).fill(0)
function App(){
  let [name, setName] = useState('')
  let state1 = useDeferredValue(name)
  return (
    <div>
      <input onChange={ (e)=>{ 
          setName(e.target.value) 
      }}/>
      {
        a.map(()=>{
          return <div>{state1}</div>
        })
      }
    </div>
  )
}
  • useDeferredValue 안에 state를 집어넣으면 그 state가 변동사항이 생겼을 때 나중에 처리해주고 처리결과는 let state에 저장

PWA

  • Progressive Web App, 웹사이트를 안드로이드/iOS 모바일 앱처럼 사용할 수 있게 만드는 일종의 웹개발 기술
    장점
  1. 스마트폰, 태블릿 바탕화면에 웹사이트를 설치 가능
  2. 오프라인에서도 동작할 수 있습니다.
    service-worker.js 라는 파일과 브라우저의 Cache storage덕분에
  3. 설치 유도 비용이 매우 적음
 npx create-react-app 프로젝트명 --template cra-template-pwa
  • 아무사이트나 파일 2개만 사이트 로컬경로에 있으면 브라우저가 PWA로 인식 (그리고 HTTPS 사이트여야합니다)
  • manifest.json과 service-worker.js 라는 이름의 파일 두개를 만들기

Q. 프로젝트 다시만들기 싫다면?

  1. 다른 폴더에 위 명령어를 이용해 프로젝트 새로 하나 만든 다음에
  2. 기존 프로젝트의 App.js App.css index.js 이런 파일들을 새 프로젝트로 복붙
    건드린 파일은 다 복붙하는데 index.js 파일은 많이 바뀐점이 좀 있어서 차이점만 잘 복붙
  3. router, redux 이런 라이브러리를 설치했다면 그것도 새프로젝트에서 다시 설치

state 변경함수 사용할 때 주의점 : async

리액트의 setState 함수 특징

function App(){
  let [name, setName] = useState('kim')
}
  • setName을 사용하려면 name이라는 state를 자유롭게 변경가능
    EX>setName('park')이런 식으로 하면 변경
  • 문제는 setName() 같은 state 변경함수들은 전부 asynchronous (비동기적)으로 처리
  • 즉, setName()이 오래걸리면 이거 제껴두고 다른 밑에 있는 코드들부터 실행하여 뭔가 예상치 못한 문제가 생길 수 있음
function App() {
    let [count, setCount] = useState(0);
    let [age, setAge] = useState(20);
    console.log(count, age);
    return (
        <div className="App">
            <div>안녕하십니까 전 {age}</div>
            <button
                onClick={() => {
                    setCount(count + 1);
                    if (count < 3) {
                        setAge(age + 1);
                    }
                }}

                누르면한살먹기
            </button>
        </div>
    );
}
  • state 변경함수는 async 하게 처리되는 함수기 때문에 완료되기까지 시간이 오래걸리면 제쳐두고 다음 코드를 실행
    ① 버튼을 세번째 누르면 setCount(count+1); 이걸 실행해서 count를 3을 만들어줌
    ② 근데 count를 3으로 만드는건 오래걸리니까 제껴두고 if ( count > 3 ) {} 실행
    ③ 이 때 count는 아직 2라서 if문 안의 setAge(age+1)이 잘 동작

이 모든 문제는 setCount()가 async 함수라서 async함수는 오래걸리면 제껴두고 다음 줄 코드부터 실행

  • state1 변경하고나서 state2를 변경하는 코드를 작성할 땐 가끔 문제 생김
  • 이걸 정확히 sync스럽게, 순차적으로 실행하고 싶을 때 해결책은 useEffect를 잘 작성하면 특정 state가 변경될 때 useEffect를 실행
function App() {
    let [count, setCount] = useState(0);
    let [age, setAge] = useState(20);
    useEffect(() => {
        if (count !== 0 && count < 3) {
            setAge(age + 1);
        }
    }, [count]);
    return (
        <div className="App">
            <div>안녕하십니까 전 {age}</div>
            <button
                onClick={() => {
                    setCount(count + 1);
                }}

                누르면한살먹기
            </button>
        </div>
    );
}
profile
자기 신뢰의 힘을 믿고 실천하는 개발자가 되고자합니다.

0개의 댓글