반응형

react에서 input에 focus out을 이용하고 싶을때는 onBlur를 이용하면 됩니다.

 

javascript react
focusin onFocus
focusout onBlur

 

focusout으로 계속 시도해보다가 onBlur를 최근에 알게되었습니다. 

아래에 예제 추가해 두었습니다.

react 깃헙에서 관련 issue도 첨부해 두겠습니다!

react-dom 라이브러리에서 focusIn, focusOut 관련 부분도 캡처 & 링크 걸어두겠습니다.

 

 

https://github.com/facebook/react/issues/6410

 

onFocusIn/onFocusOut events · Issue #6410 · facebook/react

Like mouse enter/leave, these are almost always what you want, not the onFocus and onBlur events we currently expose. I run into this semi-frequently when actually doing product work. We should add...

github.com

 

 

https://github.com/facebook/react/blob/9198a5cec0936a21a5ba194a22fcbac03eba5d1d/packages/react-dom/src/shared/ReactDOMUnknownPropertyHook.js#L36

 

GitHub - facebook/react: A declarative, efficient, and flexible JavaScript library for building user interfaces.

A declarative, efficient, and flexible JavaScript library for building user interfaces. - GitHub - facebook/react: A declarative, efficient, and flexible JavaScript library for building user interf...

github.com

 

반응형

'Frontend > React' 카테고리의 다른 글

React form 블로그 따라 다루기  (0) 2022.04.06
React keys  (0) 2022.04.02
React moment locale 적용하기 🌹  (1) 2022.04.02
React Derived State 🥲  (0) 2022.04.02
React props, state 🏔  (0) 2022.04.02
반응형

React에서 moment를 사용하는 경우 locale을 변경하고자 하면 직접 import 해주어야 합니다.

import moment from 'moment'
import 'moment/locale/fr';
import 'moment/locale/kr';

export default function App(){
  생략
}

 

moment는 시간을 다룰때 굉장히 유용한 library 입니다.

그래서 굉장히 널리 사용되고 있습니다.

 

moment는 파일사이즈가 큽니다. 

그래서 day.js와 같이 파일사이즈가 작고 moment와 쓰임새가 유사한 라이브러리를 사용하라는 정보를 접한적이 있습니다.

 

cra로 시작한 경우 moment의 locale을 변경해주려면 위와 같이 locale file을 직접 import 해주어야 합니다.

moment의 locale은 file size가 너무 커서 cra의 webpack 설정에서 exclude 되었습니다.

그래서 위와 같이 import 해주지 않으면 기본 locale인 영어만 default로 사용 가능합니다.

 

아래에 cra에서 moment 관련하여 코멘트 된 부분의 링크를 첨부하였습니다.

https://github.com/facebook/create-react-app/pull/2187/files

 

Ignore Moment.js locales by default by gaearon · Pull Request #2187 · facebook/create-react-app

We don’t often make special cases like this but Moment.js is extremely popular, and there is no end to this problem in sight. I’m okay just doing this as a “best practice” already popular in the co...

github.com

 

 

출처: https://stackoverflow.com/questions/49788259/moment-js-change-locale-not-working

 

moment.js change locale not working

My project is a react project. My website is a mutilanguage website, when I change the web language. moment.locale(lang) not working. My code is: const startDate = moment.utc(start).locale(lang);

stackoverflow.com

 

반응형

'Frontend > React' 카테고리의 다른 글

React keys  (0) 2022.04.02
React input onFocus, onBlur(focus out) 😶‍🌫️  (0) 2022.04.02
React Derived State 🥲  (0) 2022.04.02
React props, state 🏔  (0) 2022.04.02
React useReducer 🌱  (1) 2022.04.02
반응형

React 관련 자료를 살펴보다가 Derived State라는 것에 대해 알게 되었고 이를 지양해야 한다고 알게되었습니다.

Derived State가 무엇이고 해결은 어떤 방식으로 하는지 알아보려 합니다.

 

Derived State는 props로 전달받은 정보가 컴포넌트에서 state로서 정의되어 관리되는 것을 의미합니다.

derived는 ~파생된, ~유래된 이라는 뜻을 가진 영단어 입니다.

 


 

예시를 살펴보겠습니다.

import { useState } from 'react';
import './App.css';
import DerivedState from './DerivedState';

function DerivedState({ defaultEmail, onChange }) {
  const [value, setValue] = useState(defaultEmail);

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

  return (
    <form>
      <input onChange={handleChange} value={value} />
    </form>
  );
};

function App() {
  const [state, setState] = useState({ email: "test", id: '123123' })
  return (
    <div className="App">
      <DerivedState defaultEmail={state.email} />
      <button onClick={() => setState({...state, email: 'example'})}>setEmail example</button>
    </div>
  );
}

export default App;

 

 

위의 예제에서 button을 아무리 눌러도 input내의 값은 변화하지 않습니다.

input 내의 value 값은 DerivedState 컴포넌트 내에서 관리되는 state이기 때문입니다.

분명 부모 컴포넌트에서 defaultEmail props로 넘겨주었지만 부모 컴포넌트의 state를 변경해주어도 자식 컴포넌트 내부에서의 값은 변경되지 않습니다.

위의 상황은 분명히 이상합니다!!

코드의 흐름과 실제 동작이 일치하지 않는 것만 같습니다

이렇게 간단한 코드에서도 위와 같이 동작이 흐름대로 수행되지 않는다면 이는 분명 좋지 않습니다.

 


공식문서에서는 두개의 방법을 안내하고 있습니다.

 

1. 제어 컴포넌트로 만들기

2. 비제어 컴포넌트로 만들기

 

제어/비제어 컴포넌트에 관해서는 제가 이전에 포스팅 해놓은 글을 걸어두겠습니다.

2022.03.03 - [Frontend/React] - 제어 컴포넌트 vs 비제어 컴포넌트

 

1. 제어 컴포넌트로 만들기

import { useState } from "react";

function DerivedState({ email, onChange }) {
  return (
    <form>
      <input onChange={(e) => onChange(e.target.value)} value={email} />
    </form>
  );
}

function App() {
  const [email, setEmail] = useState("test");

  return (
    <div className="App">
      <DerivedState email={email} onChange={setEmail} />
      {email}
    </div>
  );
}

export default App;

위와 같이 변경하면 DerivedState는 제어 컴포넌트가 됩니다.

DerivedState 컴포넌트는 props로 전달받은 정보들을 redering만 시켜줄 뿐입니다.

모든 상태관리 로직은 부모 컴포넌트에서 정의됩니다.

 

2. 비제어 컴포넌트로 만들기

 

 

비제어 컴포넌트로 적용한 코드입니다.

자식 컴포넌트에서 props를 자신의 state로 정의하여 사용하지만 

부모 컴포넌트에서 다른 인스턴스를 선택했을때 input의 값은 초기화 됩니다.

DerivedState는 초깃값을 정의하는데에만 사용됩니다.

 

또한 부모컴포넌트에서 key값을 같이 전달해줍니다.

React에서 key값은 형제 컴포넌트간에 불필요한 렌더링을 방지하기 위해 사용되어 집니다.

형제 컴포넌트간에만 고유한 값을 가지고 있으면 값이 변경되었을때 React에서 전체를 rendering 하는 것이 아닌 key값이 변경된 데이터만 렌더링 시킵니다.

key 관련해서는 따로 포스팅 예정입니다.

 


 

Derived State에 대한 개념을 알고 피하여 맨 위의 bad example과 같은 경우를 피하는 것이 좋을것 같습니다.

 

 

출처 : https://ko.reactjs.org/docs/react-component.html#static-getderivedstatefromprops

 

React.Component – React

A JavaScript library for building user interfaces

ko.reactjs.org

https://ko.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

 

You Probably Don't Need Derived State – React Blog

React 16.4 included a bugfix for getDerivedStateFromProps which caused some existing bugs in React components to reproduce more consistently. If this release exposed a case where your application was using an anti-pattern and didn’t work properly after t

ko.reactjs.org

https://reactiver.dev/review-react-derived-state/

 

React Derived State 다시 보기

Derived State가 뭔지 이해하고, 올바른 컴포넌트 디자인 패턴에 대해 알아보자.

reactiver.dev

 

반응형
반응형

React에서 props와 state는 어떤것을 의미할까요???

 

우선

props와 state는 둘다 정보를 가지고 있는 일반 javascript 객체 입니다.

props와 state는 렌더링 결과물에 영향을 주는 정보를 가지고 있습니다.

하지만 props는 외부에서 전달을 받고 state는 컴포넌트 내부에서 관리됩니다.

 

props는 같은 input에 같은 output을 반환해 줍니다.(순수함수 같네요)

function Welcome(props) {
  return <div>welcome {props.name}</div>
}

props는 외부에서 정의되어 내부 컴포넌트로 전달됩니다.

 


state는 컴포넌트 내부에서 관리되어지는 렌더링에 영향을 주는 정보를 가지고 있는 자바스크립트 객체입니다!!

 

state는 컴포넌트 내부에서 정의되고 관리됩니다.

state의 변경은 리렌더링을 유발합니다!

state는 자식 컴포넌트에 props로 전달될 수 있습니다.

 

function Welcome() {
  const [name, setName] = useState('test');
  
  return <NameCard name={name} />
}

function NameCard({name}){
  return <>Welcome {name}</>
}

 

그러므로 렌더링을 유발시킬 수 있는 값은 state로 정의되어야 합니다!

최소한의 state를 알맞게 정의하는 것이 React 상태관리에서의 핵심입니다!

props로 전달되는 state로 부터 파생될 수 있는 값들은 state로 정의하지 않는 것이 불필요한 state의 생성을 방지할 수 있는 방법이라 생각합니다.

 

 

 

 

출처 :

https://ko.reactjs.org/docs/faq-state.html#what-is-the-difference-between-state-and-props

 

컴포넌트 State – React

A JavaScript library for building user interfaces

ko.reactjs.org

https://lucybain.com/blog/2016/react-state-vs-pros/

 

Lucy Bain

 

lucybain.com

 

반응형

'Frontend > React' 카테고리의 다른 글

React moment locale 적용하기 🌹  (1) 2022.04.02
React Derived State 🥲  (0) 2022.04.02
React useReducer 🌱  (1) 2022.04.02
React typescript 에서 dataset 이용하기 😢  (0) 2022.04.01
React setState 🌱  (0) 2022.03.27
반응형

useReducer는 useState의 대체 함수 입니다.

const [state, dispatch] = useReducer(reducer, initialState, init)

(state, action) => newState의 형태로 reducer를 입력받고 dispatch 메서드와 짝의 형태로 현재 state를 반환해 줍니다.

자세한 내용은 코드를 직접 살펴보겠습니다.

 

react 공식문서에서는 state로직이 복잡해지면 리듀서의 사용, 커스텀 hook을 고려하라고 안내하고 있습니다.

아래에서는 state의 복잡한 상황을 정의해 보고 개선하는 과정을 살펴보겠습니다.

개선된 코드가 개선되지 않았다고 느낄수도 있습니다.

모든 상황에서 장단점이 존재하기 때문입니다.

정답을 하나로 규정하기 보다는 팀의 컨벤션에 맞게, 자신의 스타일에 맞게 코드를 짜는것을 추천드립니다.

또 그에따른 유연한 태도를 겸비해야 하지 않을까 생각합니다.

 

총 4개의 state 정의

 

1. component에서 4개의 useState로 관리

2. 하나의 state로 관리

3. useReducer 이용

(세 컴포넌트 모두 동작은 정상적으로 수행합니다)

 

 

위의 1 -> 2 -> 3 번의 순서대로 코드를 살펴보고 각각의 장단점을 살펴보겠습니다.

 


 

1. 4개의 state로 관리

import { useState } from "react";

const CounterWithoutReducer = () => {
  const [countOne, setCountOne] = useState(0)
  const [countTwo, setCountTwo] = useState(0)
  const [countThree, setCountThree] = useState(0);
  const [booleanValue, setBooleanValue] = useState(false)

  const numberHandler = (type, setter) => {
    if (type === 'plus') {
      setter(prev => prev + 1)
    }

    if (type === 'minus') {
      setter((prev) => prev - 1);
    }
  }

  const booleanHandler = () => {
    setBooleanValue(prev => !prev)
  }

  return (
    <>
      <div>
        CountOne: {countOne}
        <button onClick={() => numberHandler("plus", setCountOne)}>+</button>
        <button onClick={() => numberHandler("minus", setCountOne)}>-</button>
      </div>
      <div>
        CountTwo: {countTwo}
        <button onClick={() => numberHandler("plus", setCountTwo)}>+</button>
        <button onClick={() => numberHandler("minus", setCountTwo)}>-</button>
      </div>
      <div>
        CountThree: {countThree}
        <button onClick={() => numberHandler("plus", setCountThree)}>+</button>
        <button onClick={() => numberHandler("minus", setCountThree)}>-</button>
      </div>
      <div>
        Boolean: {booleanValue === false ? 'false' : 'true'}
        <button onClick={() => booleanHandler()}>toggle</button>
      </div>
    </>
  );
};

export default CounterWithoutReducer;

4개의 state로 관리했을때의 코드입니다.

state의 성격에 따라 3,1 로 나눌수 있을것 같습니다.

각각의 성격에 따라 handler를 정의할 수 있지만 

공통으로 사용하는 handler에서 매번 setter를 보내주어야 합니다. 

 

만약에 기존의 state들과 다른 성격의 state가 추가되면 어떻게 될까요??

useState, handler를 새로 정의해주어야 합니다.

 

현재 상태에서 customHook으로의 정의또한 어려워 보입니다.

각각의 state를 별개로 관리해야하는 현재의 상황이고 이렇게 되면 customHook은 4개가 생깁니다.

customHook을 활용하기는 어려울것만 같습니다.(물론 상황에 따라 다를 수 있습니다. 그러므로 현재의 상황으로만 가정하겠습니다.)

 

현재는 코드의 난이도가 높지 않아 문제가 없지만 복잡해졌을때는 다수의 state가 정의된 곳을 파악해야 하고 state의 확장에 장애물이 있을것 같습니다.

즉 유지보수의 비용이 상대적으로 증가될것 같습니다.

하지만 팀의 컨벤션이 위와 같다면 팀의 컨벤션을 따르거나 리팩토링을 타협해야 할것만 같네요

 


 

2. 1개의 state로 관리

 

import { useState } from "react";

const CounterWithState = () => {
  const [state, setState] = useState({ countOne: 0, countTwo: 0, countThree: 0, booleanValue: 0 })
  
  const numberHandler = (type) => {
    if (type === "plusOne") {
      setState(prev => ({
        ...prev,
        countOne: prev.countOne + 1,
      }))
    }

    if (type === "minusOne") {
      setState(prev => ({
        ...prev,
        countOne: prev.countOne - 1,
      }))
    }

    if (type === "plusTwo") {
      setState((prev) => ({
        ...prev,
        countTwo: prev.countTwo + 1,
      }));
    }

    if (type === "minusTwo") {
      setState((prev) => ({
        ...prev,
        countTwo: prev.countTwo - 1,
      }));
    }

    if (type === "plusThree") {
      setState((prev) => ({
        ...prev,
        countThree: prev.countThree + 1,
      }));
    }

    if (type === "minusThree") {
      setState((prev) => ({
        ...prev,
        countThree: prev.countThree - 1,
      }));
    }
  };

  const booleanHandler = () => {
    setState(prev => ({
      ...prev,
      booleanValue: !prev.booleanValue,
    }))
  };

  return (
    <>
      <div>
        CountOne: {state.countOne}
        <button onClick={() => numberHandler("plusOne")}>+</button>
        <button onClick={() => numberHandler("minusOne")}>-</button>
      </div>
      <div>
        CountTwo: {state.countTwo}
        <button onClick={() => numberHandler("plusTwo")}>+</button>
        <button onClick={() => numberHandler("minusTwp")}>-</button>
      </div>
      <div>
        CountThree: {state.countThree}
        <button onClick={() => numberHandler("plusThree")}>+</button>
        <button onClick={() => numberHandler("minusThree")}>-</button>
      </div>
      <div>
        Boolean: {state.booleanValue === false ? "false" : "true"}
        <button onClick={() => booleanHandler()}>toggle</button>
      </div>
    </>
  );
};

export default CounterWithState;

4개의 state를 key, value 로 하나의 객체에 정의해 주었고 handler를 성격에 맞게 정의해 주었습니다.

따라서 2개의 handler를 정의해 주었습니다. (물론 위의 코드보다 더 효율적으로 작성할 수 있습니다!!)

 

하나의 useState hook에서 관리하므로 state간의 관계 파악이 1번 보다는 수월할 것 같습니다.

state의 성격에 따라 알맞은 개수의 state hook으로 생성하면 더 좋은 방법일것 같네요

2번의 코드에서는 handler만 util 함수로 이동시키면 컴포넌트 내부의 코드를 파악하기도 수월해질것 같습니다.

 

하지만 저는 코드를 파악해야 할때 여러 파일들을 탐색해야 하는 것을 선호하지 않습니다.

그런 의미에서 위에서 언급한 컴포넌트에서 사용하는 util이 외부에 있는 것을 선호하지 않습니다.

컴포넌트 내부에 handler를 정의하는 것이 코드의 파악에 있어 더 수월할 것이기 때문입니다.

 

메모리에는 지역성 이란 개념이 있습니다. 

시간 지역성, 공간 지역성이 있는데

시간 지역성은 최근에 참조된 영역은 곧 다시 참조될 가능성이 크고

공간 지역성은 최근에 참조된 영역의 주위가 다시 참조될 가능성이 크다는 이야기 입니다.

(개념이 틀리다면 말씀해주세요!! 잘못 알고 있을수도 있습니다.)

 

코드에서도 이 원리가 적용된다고 생각합니다.

하나의 코드를 수정하면 그 근처의 코드들이 수정되기 마련입니다.

이런 원리에 근거할때 수정되야 하는 코드가 가까이에 위치한다면 유지보수에 긍정적으로 작용할 것 같습니다.

 

다시 본론으로 돌아와서 2번의 hook을 customHook으로 분리할 수는 있을까요??

customHook으로의 분리는 가능할것 같네요

 

 

customHook 으로 분리한 코드

import { useState } from 'react';

const useMultipleState = () => {
  const [state, setState] = useState({
    countOne: 0,
    countTwo: 0,
    countThree: 0,
    booleanValue: 0,
  });

  const numberHandler = (type) => {
    if (type === "plusOne") {
      setState((prev) => ({
        ...prev,
        countOne: prev.countOne + 1,
      }));
    }

    if (type === "minusOne") {
      setState((prev) => ({
        ...prev,
        countOne: prev.countOne - 1,
      }));
    }

    if (type === "plusTwo") {
      setState((prev) => ({
        ...prev,
        countTwo: prev.countTwo + 1,
      }));
    }

    if (type === "minusTwo") {
      setState((prev) => ({
        ...prev,
        countTwo: prev.countTwo - 1,
      }));
    }

    if (type === "plusThree") {
      setState((prev) => ({
        ...prev,
        countThree: prev.countThree + 1,
      }));
    }

    if (type === "minusThree") {
      setState((prev) => ({
        ...prev,
        countThree: prev.countThree - 1,
      }));
    }
  };

  const booleanHandler = () => {
    setState((prev) => ({
      ...prev,
      booleanValue: !prev.booleanValue,
    }));
  };

  return {
    state,
    numberHandler,
    booleanHandler,
  }
}

export default useMultipleState;
import useMultipleState from "./hooks/useMultipleState";

const CounterWithState = () => {
  const {state, numberHandler, booleanHandler } = useMultipleState();
  return (
    <>
      <div>
        CountOne: {state.countOne}
        <button onClick={() => numberHandler("plusOne")}>+</button>
        <button onClick={() => numberHandler("minusOne")}>-</button>
      </div>
      <div>
        CountTwo: {state.countTwo}
        <button onClick={() => numberHandler("plusTwo")}>+</button>
        <button onClick={() => numberHandler("minusTwp")}>-</button>
      </div>
      <div>
        CountThree: {state.countThree}
        <button onClick={() => numberHandler("plusThree")}>+</button>
        <button onClick={() => numberHandler("minusThree")}>-</button>
      </div>
      <div>
        Boolean: {state.booleanValue === false ? "false" : "true"}
        <button onClick={() => booleanHandler()}>toggle</button>
      </div>
    </>
  );
};

export default CounterWithState;

 


 

3. useReducer 이용

import useCounter from "./hooks/useCounter";

const CounterTest = () => {
  const {state, action} = useCounter();
  
  return (
    <>
      <div>
        CountOne: {state.countOne}
        <button onClick={() => action("plusCountOne")}>+</button>
        <button onClick={() => action("minusCountOne")}>-</button>
      </div>
      <div>
        CountTwo: {state.countTwo}
        <button onClick={() => action("plusCountTwo")}>+</button>
        <button onClick={() => action("minusCountTwo")}>-</button>
      </div>
      <div>
        CountThree: {state.countThree}
        <button onClick={() => action("plusCountThree")}>+</button>
        <button onClick={() => action("minusCountThree")}>-</button>
      </div>
      <div>
        Boolean: {state.booleanValue === false ? "false" : "true"}
        <button onClick={() => action("toggleBoolean")}>toggle</button>
      </div>
    </>
  );
};

export default CounterTest;
import { useReducer } from 'react';

const useCounter = () => {
  const reducer = (state, action) => {
    if (action === "plusCountOne") {
      state = {
        ...state,
        countOne: state.countOne + 1,
      };
    }
    if (action === "minusCountOne") {
      state = {
        ...state,
        countOne: state.countOne - 1,
      };
    }
    if (action === "plusCountTwo") {
      state = {
        ...state,
        countTwo: state.countTwo + 1,
      };
    }
    if (action === "minusCountTwo") {
      state = {
        ...state,
        countTwo: state.countTwo - 1,
      };
    }
    if (action === "plusCountThree") {
      state = {
        ...state,
        countThree: state.countThree + 1,
      };
    }
    if (action === "minusCountThree") {
      state = {
        ...state,
        countThree: state.countThree - 1,
      };
    }
    if (action === "toggleBoolean") {
      state = {
        ...state,
        booleanValue: !state.booleanValue,
      };
    }

    return state;
  };

  const initialState = {
    countOne: 0,
    countTwo: 0,
    countThree: 0,
    booleanValue: false,
  };
  
  const [state, dispatch] = useReducer(reducer, initialState);

  return {
    state, 
    action: dispatch
  };
}

export default useCounter;

 

useReducer를 적용한 코드 입니다.

reducer내부의 코드가 길어지는 것은 단점이라 생각합니다.

하지만 customHook으로 분리가 가능한 점과 

state의 확장에 유연하며 

rendering 부분에서도 코드의  파악에 좀 더 용이하여 유지보수에 도움이 된다고 생각합니다.

 

하나의 컴포넌트에서 관리해야 하는 state가 복수개 있을때 저는 리듀서의 적용을 선호합니다.

여러개 컴포넌트를 관리해야할때 1번과 같은 방법보다는 각각 customHook으로 관리하여 좀 더 코드의 파악에 도움이 되는 것이 좋다고 생각합니다.

 

물론 적용은 각자가 선호하는 방식대로 하는 것이 좋지 않을까 생각합니다.

 

 

출처 : https://ko.reactjs.org/docs/hooks-reference.html#usereducer

 

Hooks API Reference – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

반응형

'Frontend > React' 카테고리의 다른 글

React Derived State 🥲  (0) 2022.04.02
React props, state 🏔  (0) 2022.04.02
React typescript 에서 dataset 이용하기 😢  (0) 2022.04.01
React setState 🌱  (0) 2022.03.27
React.memo 🌱  (0) 2022.03.27
반응형

필요한 데이터를 html의 data 속성을 이용하여 주로 담아놓곤 하였습니다.

이를 React with typescript에서 적용할 때 막혔던 부분에 대한 내용입니다.

 

위와 같이 사용하려 시도했었습니다.

하지만 계속 에러를 뱉어냈고 이를 이해하지 못했었습니다.

 

이유는 다음과 같습니다. 

event의 target이 document, element, window등의 요소가 될 수 있기 때문입니다. 

그래서 아래와 같이 event의 target이 내가 정한 요소의 instance가 아니라면 early return을 하여 런타임에서 에러가 없을 수 있게 하였습니다.

 

 

위의 이미지속의 코드에서 e.target의 instance를 검사하여 div 요소가 아니면 함수를 탈출하게 하였습니다.

위와 같이 작성후 코드레벨상에서의 에러 && 런타임에서의 에러 없이 동작할 수 있도록 하였습니다.

 

 

 

 

 

 

출처 : https://stackoverflow.com/questions/49631688/property-dataset-does-not-exist-on-type-eventtarget

 

Property 'dataset' does not exist on type 'EventTarget'

When trying to access the dataset on a button after a click, I get this^ error. linkProvider = (ev: React.SyntheticEvent<EventTarget>) => { console.debug('ev.target', ev.target.dataset[...

stackoverflow.com

 

반응형

'Frontend > React' 카테고리의 다른 글

React props, state 🏔  (0) 2022.04.02
React useReducer 🌱  (1) 2022.04.02
React setState 🌱  (0) 2022.03.27
React.memo 🌱  (0) 2022.03.27
React useCallback, useMemo  (0) 2022.03.27
반응형
  • 리액트에서 setState는 비동기 동작인가요, 동기동작인가요?
  • setState가 비동기 동작을 취했을 때 얻을 수 있는 이점은 무엇인가요?

setState는 비동기 동작입니다.

setState가 비동기로 동작을 하는 이유는 렌더링을 더 적게 하면서 화면에 최신값을 보여주기 위함입니다.

 

useState의 state hook으로 살펴보겠습니다.

import {useState} from 'react';

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

  function handleAlertClick() {
    setCount(1);
    setCount(2);
    setCount(3);
  }
  
  console.log('rerendered');
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={handleAlertClick}>Show alert</button>
    </div>
  );
}

export default Example;

위와 같은 컴포넌트가 있습니다.

이때 handleAlertClick이 실행되면 count는 3이 되고 rerendered는 한번만 출력됩니다.

 

import {useState} from 'react';

function Example() {
  const [count, setCount] = useState(0);
  const [test, setTest] = useState(0);
  const [code, setCode] = useState(0);

  function handleAlertClick() {
    setCount(count + 1);
    setTest(test + 1);
    setCode(code + 1);
  }

  console.log('rerendered')

  return (
    <div>
      <p>You clicked {code} times</p>
      <p>You clicked {count} times</p>
      <p>You clicked {test} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={handleAlertClick}>Show alert</button>
    </div>
  );
}

export default Example;

위와 같은 컴포넌트가 또 있습니다. 

handleAlertClick이 실행되면 count, test, code의 값들이 1로 바뀌고 rerendered는 1번만 출력됩니다.

 

만약 setState가 동기적인 실행을 한다면 어떻게 될까요??

setCount가 실행되고 렌더링이 일어나고

setTest가 실행되고 렌더링이 일어나고

setCode가 실행되고 렌더링이 일어나게 될것입니다.

 

분명 비동기로 실행될때와 결과는 같지만 렌더링이 3번 일어나게 되어 같은 결과에 더 많은 렌더링이 일어나게 됩니다.

따라서 하나의 핸들러 함수에서 상태가 변화되는 경우 한번에 모아서 실행하기 위해 비동기로 동작을 하게 됩니다.

 

이러한 것을 배칭이라 합니다.

배칭은 React가 더 나은 성능을 위해 여러개의 state 업데이트를 하나의 리렌더링으로 묶는것을 의미합니다.

 

결국 비동기로 동작함으로써 더 적은 렌더링으로 동기적 실행을 했을때와 같은 화면을 보여줄 수 있습니다.

 

그렇다면 아래의 결과는 어떻게 출력 될까요?

import {useState} from 'react';

function Example() {
  const [count, setCount] = useState(0);
  const [test, setTest] = useState(0);
  const [code, setCode] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      setCount(count + 1);
      setTest(test + 1);
      setCode(code + 1);
    }, 0)  
  }

  console.log('rerendered')

  return (
    <div>
      <p>You clicked {code} times</p>
      <p>You clicked {count} times</p>
      <p>You clicked {test} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={handleAlertClick}>Show alert</button>
    </div>
  );
}

export default Example;

 

handleAlertClick 함수가 실행되면 rerendered는 3번 출력 됩니다.

렌더링이 3번 일어나게 되는 것이죠

리액트 에서는 콜백함수로 일어나는 state를 아직 배칭할수가 없습니다.

 

콜백함수에서도 배칭할 수 있게 해주는 것이 react의 18버전에 나왔습니다.

아래는 react18에서의 배칭에 관한 글입니다.

react 18을 접하고 나중에 주요 기능에 대해 기록할 수 있도록 하겠습니다.

https://immigration9.github.io/react/2021/06/12/automatic-batching-react.html

 

React 18: 렌더링 최적화를 위한 자동 배칭

Introduction

immigration9.github.io

 

반응형

'Frontend > React' 카테고리의 다른 글

React useReducer 🌱  (1) 2022.04.02
React typescript 에서 dataset 이용하기 😢  (0) 2022.04.01
React.memo 🌱  (0) 2022.03.27
React useCallback, useMemo  (0) 2022.03.27
React 의존성 배열 🌱  (0) 2022.03.27
반응형
  • 아래와 같이 자식 컴포넌트를 React.memo로 감쌌을 때 props를 통해서 전달되어지는 함수에 useCallback을 사용한 경우와 사용하지 않은 경우에 차이가 있나요?
function Parent(){
	const handleClick = useCallback(() => { ... }, []); // (1)
	const handleClick = () =>{ ... } // (2)

	return <Child onClick={handleClick} />
}

function Child({ onClick }) {
	return <div onClick={onClick} />
}
export default React.memo(Child);

=> React.memo로 감싸게 되면 props로 전달받은 값이 변화하지 않으면 렌더링을 발생시키지 않습니다.

useCallback을 사용하지 않은경우 부모 컴포넌트가 렌더링 될때마다 자식 컴포넌트도 재렌더링 됩니다.

useCallback을 사용하는 경우에는 부모 컴포넌트가 렌더링 되어도 자식 컴포넌트는 재렌더링 되지 않습니다.

 

useCallback을 사용하지 않는 경우에는 handleClick이 렌더링 시마다 값의 주소값이 변경되게 됩니다.

props로 전달받은 Child 컴포넌트에서는 주소값이 변경되었으므로 재렌더링을 발생시킵니다.

 

useCallback을 사용한 경우에는 의존성 배열의 값이 변하지 않으면 handleClick에는 새로운 주소값을 가지지 않습니다.

props로 전달되는 handleClick의 주소값이 변경되지 않았으므로 Child는 재렌더링 되지 않습니다.

React에서는 값의 비교에서 Object.is 알고리즘을 사용합니다.

 

  • React.memo의 특징에 대해서 설명해주세요.

React.memo는 고차 컴포넌트 입니다.

일반 컴포넌트는 props를 ui를 반환하는 반면에 고차 컴포넌트는 새로운 컴포넌트를 반환합니다.

 

만약 컴포넌트가 동일한 props로 동일한 ui를 반환한다면 새로 컴포넌트를 렌더링 할 필요가 없게됩니다.

이때 React.memo를 사용하여 마지막 렌더링 결과값을 재사용 할 수 있습니다.

React.memo는 props의 변화에만 영향을 받습니다.

React.memo에는 인자를 두개 받을수 있습니다.

React.memo(component, fn)

만약 두번째 인자가 생략된다면 이전 props와 얕은 비교만을 수행합니다.

두번째 인자에 비교함수를 넣어 React.memo의 비교를 커스텀 할 수 있습니다.

function MyComponent(props) {
	// props를 사용하여 렌더링
}

function areEqual(prevProps, nextProps) {
  // nextProps가 prevProps와 동일한 값을 가지면 true를 반환하고, 그렇지 않다면 false를 반환
}

export default React.memo(MyComponent, areEqual);

 

 

https://ko.reactjs.org/docs/react-api.html

 

React 최상위 API – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

반응형

'Frontend > React' 카테고리의 다른 글

React typescript 에서 dataset 이용하기 😢  (0) 2022.04.01
React setState 🌱  (0) 2022.03.27
React useCallback, useMemo  (0) 2022.03.27
React 의존성 배열 🌱  (0) 2022.03.27
React 피드백 내용 정리  (0) 2022.03.04

+ Recent posts