React에서 후크를 사용하여 컴포넌트를 강제로 다시 렌더링하려면 어떻게해야합니까?


116

아래 후크 예제 고려

   import { useState } from 'react';

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

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

기본적으로 this.forceUpdate () 메서드를 사용하여 아래 예제와 같이 React 클래스 구성 요소에서 구성 요소를 즉시 다시 렌더링하도록합니다.

    class Test extends Component{
        constructor(props){
             super(props);
             this.state = {
                 count:0,
                 count2: 100
             }
             this.setCount = this.setCount.bind(this);//how can I do this with hooks in functional component 
        }
        setCount(){
              let count = this.state.count;
                   count = count+1;
              let count2 = this.state.count2;
                   count2 = count2+1;
              this.setState({count});
              this.forceUpdate();
              //before below setState the component will re-render immediately when this.forceUpdate() is called
              this.setState({count2: count
        }

        render(){
              return (<div>
                   <span>Count: {this.state.count}></span>. 
                   <button onClick={this.setCount}></button>
                 </div>
        }
 }

하지만 내 쿼리는 위의 기능 구성 요소를 후크로 즉시 다시 렌더링하도록 강제 할 수 있습니까?


1
this.forceUpdate()? 를 사용하는 원본 구성 요소의 버전을 게시 할 수 있습니까 ? 그것 없이도 같은 일을 할 수있는 방법이있을 것입니다.
Jacob

setCount의 마지막 줄이 잘립니다. 현재 상태에서 setCount의 목적이 무엇인지 명확하지 않습니다.
Estus Flask 2018

이것은 this.forceUpdate (); 이후의 작업입니다. 난 그냥 내 질문에 () this.forceUpdate에 대해 설명하는 것을 추가
Hemadri Dasari

그만한 가치가있는 이유 : 수동 재 렌더링이 필요하다고 생각했기 때문에이 문제와 씨름하고 있었고 마침내 외부에서 보유한 변수를 상태 후크로 옮기고 설정 기능을 활용해야한다는 것을 깨달았습니다. 다시 렌더링합니다. 결코 필요 하지 않다는 말은 아니지만 특정 사용 사례에서 실제로 필요한지 확인하기 위해 세 번째와 네 번째 살펴볼 가치가 있습니다.
Alexander Nied 20.04.04

답변:


78

내부적으로 사용하기 때문에 useState또는 으로 가능합니다 .useReduceruseStateuseReducer

const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);

forceUpdate정상적인 상황에서 사용하기위한 것이 아니라 테스트 또는 기타 뛰어난 경우에만 사용할 수 있습니다. 이 상황은보다 일반적인 방법으로 해결할 수 있습니다.

setCount부적절한 사용의 예입니다 forceUpdate, setState성능상의 이유로 비동기 및 상태 업데이트가 제대로 수행되지 않았다해서 동기로 강제되어서는 안된다. 상태가 이전에 설정된 상태에 의존하는 경우 업데이트 기능 으로 수행해야합니다 .

이전 상태를 기반으로 상태를 설정해야하는 경우 아래 업데이트 프로그램 인수에 대해 읽어보세요.

<...>

업데이터 함수에서 수신 한 상태와 소품은 모두 최신 상태를 보장합니다. 업데이트 프로그램의 출력은 상태와 얕게 병합됩니다.

setCount 목적이 불명확하기 때문에 예시가 아닐 수 있지만 이것은 업데이트 프로그램 기능의 경우입니다.

setCount(){
  this.setState(({count}) => ({ count: count + 1 }));
  this.setState(({count2}) => ({ count2: count + 1 }));
  this.setState(({count}) => ({ count2: count + 1 }));
}

이것은 콜백으로 사용되는 함수를 더 잘 기억해야한다는 점을 제외하고는 후크로 1 : 1로 변환됩니다.

   const [state, setState] = useState({ count: 0, count2: 100 });

   const setCount = useCallback(() => {
     setState(({count}) => ({ count: count + 1 }));
     setState(({count2}) => ({ count2: count + 1 }));
     setState(({count}) => ({ count2: count + 1 }));
   }, []);

어떻게 const forceUpdate = useCallback(() => updateState({}), []);작동합니까? 업데이트를 강요합니까?
Dávid Molnár

2
@ DávidMolnár는을 useCallback메모 forceUpdate하므로 구성 요소 수명 동안 일정하게 유지되며 안전하게 소품으로 전달할 수 있습니다. updateState({})forceUpdate호출에서 새 객체로 상태를 업데이트하면 다시 렌더링됩니다. 예, 호출 될 때 업데이트를 강제합니다.
Estus Flask

3
따라서 useCallback부품은 실제로 필요하지 않습니다. 그것 없이는 잘 작동합니다.
Dávid Molnár

원시 데이터 유형 updateState(0)이기 때문에 작동하지 0않습니까? 객체 여야 updateState([])합니까, 아니면 (즉, 배열 사용) 작동합니까?
Andru

1
@Andru 예, 0 === 0이기 때문에 상태가 한 번 업데이트됩니다. 예, 배열도 객체이기 때문에 작동합니다. updateState(Math.random())이나 카운터 와 같이 동등성 검사를 통과하지 못하는 것은 무엇이든 사용할 수 있습니다 .
Estus Flask

29

다른 사람들이 언급했듯이 useState작동합니다. mobx-react-lite 가 업데이트를 구현 하는 방법 은 다음과 같습니다. 유사한 작업을 수행 할 수 있습니다.

새 후크 정의, useForceUpdate-

import { useState, useCallback } from 'react'

export function useForceUpdate() {
  const [, setTick] = useState(0);
  const update = useCallback(() => {
    setTick(tick => tick + 1);
  }, [])
  return update;
}

구성 요소에서 사용-

const forceUpdate = useForceUpdate();
if (...) {
  forceUpdate(); // force re-render
}

참조 https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.tshttps://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver .ts


2
후크에서 이해 한 바에 useForceUpdate따르면 함수가 다시 렌더링 될 때마다 새 함수를 반환하므로 작동하지 않을 수 있습니다 . 들어 forceUpdateA의 사용하는 경우 작업에 useEffect, 그것은 반환해야합니다 useCallback(update)참조 kentcdodds.com/blog/usememo-and-usecallback
마틴 Ratinaud

감사합니다, @MartinRatinaud-예, useCallback (?)없이 메모리 누수가 발생할 수 있습니다-수정되었습니다.
Brian Burns 19 년

27

일반적으로 업데이트를 트리거하려는 모든 상태 처리 방법을 사용할 수 있습니다.

TypeScript 사용

codesandbox 예

useState

const forceUpdate: () => void = React.useState()[1].bind(null, {})  // see NOTE below

useReducer

const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void

커스텀 후크로

이처럼 선호하는 접근 방식을 포장하십시오.

function useForceUpdate(): () => void {
  return React.useReducer(() => ({}), {})[1] as () => void // <- paste here
}

어떻게 작동합니까?

" 업데이트를 트리거하려면 " 은 어떤 값이 변경되었으며 컴포넌트를 다시 렌더링해야한다고 React 엔진에 알리는 것을 의미합니다.

[, setState]from useState()에는 매개 변수 가 필요합니다. 새로운 객체를 바인딩하여 제거합니다 {}.
() => ({})in useReducer은 액션이 전달 될 때마다 새로운 객체를 반환하는 더미 감속기입니다.
{} (새 객체) 는 상태에서 참조를 변경하여 업데이트를 트리거하기 위해 필요합니다.

추신 : 내부적으로 useState포장 useReducer합니다. 출처

참고 : useState와 함께 .bind를 사용하면 렌더링간에 함수 참조가 변경됩니다. 여기 에서이미 설명했듯이 useCallback 안에 넣을수는 있지만 섹시한 one-liner ™ 는 아닙니다. Reducer 버전은 이미 렌더링간에 참조 동등성을 유지 합니다. props에서 forceUpdate 함수를 전달하려는 경우 중요합니다.

일반 JS

const forceUpdate = React.useState()[1].bind(null, {})  // see NOTE above
const forceUpdate = React.useReducer(() => ({}))[1]

17

@MinhKha의 답변에 대한 대안 :

다음으로 훨씬 더 깨끗해질 수 있습니다 useReducer.

const [, forceUpdate] = useReducer(x => x + 1, 0);

사용법 : forceUpdate()-매개 변수없는 클리너


13

다음과 같이 useState를 간단히 정의 할 수 있습니다.

const [, forceUpdate] = React.useState(0);

그리고 사용법 : forceUpdate(n => !n)

이 도움을 바랍니다!


7
forceUpdate가 렌더 당 짝수 번 호출되면 실패합니다.
Izhaki

값을 계속 증가 시키십시오.
게리

2
오류가 발생하기 쉬우므로 제거하거나 편집해야합니다.
slikts

11

React Hooks FAQ 공식 솔루션 forceUpdate:

const [_, forceUpdate] = useReducer((x) => x + 1, 0);
// usage
<button onClick={forceUpdate}>Force update</button>

작업 예


10

구성 요소가 상태 및 소품에만 의존하도록하는 것이 바람직하며 예상대로 작동하지만 구성 요소를 강제로 다시 렌더링하는 함수가 필요한 경우 다음을 사용할 수 있습니다. useState 하는 함수가 필요한 경우 후크를 필요할 때 함수를 호출 .

const { useState, useEffect } = React;

function Foo() {
  const [, forceUpdate] = useState();

  useEffect(() => {
    setTimeout(forceUpdate, 2000);
  }, []);

  return <div>{Date.now()}</div>;
}

ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>


좋아, 그런데 왜 React가 this.forceUpdate (); 구성 요소가 이전 버전에서 setState로 다시 렌더링 될 때 처음에?
Hemadri Dasari 2018

1
@ Think-Twice 개인적으로 사용하지 않았고 지금은 좋은 사용 사례를 생각할 수 없지만 정말 특별한 사용 사례에 대한 탈출구라고 생각합니다. "일반적으로 당신의 모든 사용을 방지하기 위해 시도해야 forceUpdate()만 읽기 this.propsthis.staterender()."
Tholle 2018

1
동의합니다. 나는 심지어 내 경험에 사용한 적이 있지만 나는 그래서 그냥 같은 일이 후크에서 수행 할 수있는 방법을 이해하려고 노력 작동 방법을 알고
Hemadri Dasari

1
@Tholle and I can't think of a good use case for it right now 나는 상태가 React에 의해 제어되지 않는 경우 어떻습니까 ? 저는 Redux를 사용하지 않지만 일종의 강제 업데이트를 수행해야한다고 가정합니다. 개인적으로 프록시를 사용하여 상태를 유지하면 구성 요소가 prop 변경 사항을 확인한 다음 업데이트 할 수 있습니다. 매우 효율적으로 작동하는 것 같습니다. 예를 들어 모든 구성 요소는 프록시에 의해 제어되고이 프록시는 SessionStorage에 의해 지원되므로 사용자가 웹 페이지를 새로 고쳐도 드롭 다운 상태 등이 유지됩니다. IOW : 저는 state를 전혀 사용하지 않습니다. 모든 것은 props로 제어됩니다.
키스

8

간단한 코드

const forceUpdate = React.useReducer(bool => !bool)[1];

사용하다:

forceUpdate();

5

React가 JSX 코드에서 부울인쇄하지 않는다는 사실을 활용하여 일반 후크를 사용하여 다시 렌더링을 강제 할 수 있습니다.

// create a hook
const [forceRerender, setForceRerender] = React.useState(true);

// ...put this line where you want to force a rerender
setForceRerender(!forceRerender);

// ...make sure that {forceRerender} is "visible" in your js code
// ({forceRerender} will not actually be visible since booleans are
// not printed, but updating its value will nonetheless force a
// rerender)
return (
  <div>{forceRerender}</div>
)


1
이 경우 setBoolean이 두 번 변경되면 React.useEffect가 업데이트를 다시 인식하지 못할 수 있습니다.
alma

내가 이해하는 한 React는 부울이 다시 빠르게 다시 전환 되더라도 모든 부울 업데이트를 페이지를 다시 렌더링하는 이유로 취급합니다. 즉, React는 물론 표준이 아니며이 인스턴스에서 정확히 작동하는 방식은 정의되지 않았으며 변경 될 수 있습니다.
Fergie

1
모르겠어요. 어떤 이유로 나는이 버전에 매력을 느낍니다. 그것은 나를 간지럽 힙니다 :-). 게다가 순수함을 느낍니다. 내 JSX가 의존하는 것이 변경되었으므로 다시 렌더링합니다. 투명성은 IMO를 손상시키지 않습니다.
Adrian Bartholomew

4

잠재적 인 옵션은 key. 키를 업데이트하면 구성 요소의 렌더링이 트리거됩니다 (이전에 업데이트하지 못함).

예를 들면 :

const [tableKey, setTableKey] = useState(1);
...

useEffect(() => {
    ...
    setTableKey(tableKey + 1);
}, [tableData]);

...
<DataTable
    key={tableKey}
    data={tableData}/>

1
상태 값과 다시 렌더링 요구 사항 사이에 1 : 1 관계가있는 경우 가장 깔끔한 방법입니다.
jaimefps

1
이것은 쉬운 해결책입니다
Priyanka V


3

한 줄 솔루션 :

const useForceUpdate = () => useState()[1];

useState 는 한 쌍의 값을 반환합니다. 현재 상태와이를 업데이트하는 함수 인 statesetter , 여기서는 다시 렌더링을 강제하기 위해 setter 만 사용합니다.


2

내 변형은 forceUpdatea를 통한 counter것이 아니라 객체를 통한 것입니다.

// Emulates `forceUpdate()`
const [unusedState, setUnusedState] = useState()
const forceUpdate = useCallback(() => setUnusedState({}), [])

왜냐하면 {} !== {}매번.


무엇입니까 useCallback()? 그게 어디에서 왔습니까? 죄송합니다. 지금 그것을 볼 ...
zipzit

2

이렇게하면 구성 요소에 따라 3 번 렌더링됩니다 (요소가 동일한 배열은 같지 않음).

const [msg, setMsg] = useState([""])

setMsg(["test"])
setMsg(["test"])
setMsg(["test"])

1
배열에 항목을 넣을 필요조차 없다고 생각합니다. 빈 배열은 객체와 마찬가지로 단순히 다른 참조에 의해 다른 빈 배열과 완전히 동일하지 않습니다.
Qwerty

네, 단지 데이터 전달의 방법으로 그것을 보여주고 싶었다
Janek Olszak

2

react-tidy다음과 같은 작업을 수행하기위한 사용자 지정 후크가 있습니다 useRefresh.

import React from 'react'
import {useRefresh} from 'react-tidy'

function App() {
  const refresh = useRefresh()
  return (
    <p>
      The time is {new Date()} <button onClick={refresh}>Refresh</button>
    </p>
  )
}

이 후크에 대해 자세히 알아보기

면책 조항 나는이 도서관의 작가입니다.


1

일반 React Class 기반 구성 요소 forceUpdate에 대해서는 URL 에서 api에 대한 React Docs를 참조하십시오 . 문서는 다음과 같이 언급합니다.

일반적으로 forceUpdate ()의 모든 사용을 피하고 render ()의 this.props 및 this.state에서만 읽어야합니다.

그러나 문서에서도 다음과 같이 언급됩니다.

render () 메서드가 다른 데이터에 의존하는 경우 forceUpdate ()를 호출하여 컴포넌트를 다시 렌더링해야한다고 React에 알릴 수 있습니다.

따라서 사용 사례는 forceUpdate 가 드물고 사용해 본 적이 없지만 내가 작업 한 일부 레거시 기업 프로젝트에서 다른 개발자가 사용하는 것을 본 적이 있습니다.

따라서 Functional Components의 동등한 기능에 대해서는 URL 에서 HOOKS 용 React Docs를 참조하십시오 . 위의 URL에 따라 "useReducer"후크를 사용하여forceUpdate 기능 구성 요소에 대한 기능 .

실제로 동작하는 코드 샘플 that does not use state or props에서 CodeSandbox에서도 사용할 수 아래에 제공되고, URL

import React, { useReducer, useRef } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  // Use the useRef hook to store a mutable value inside a functional component for the counter
  let countref = useRef(0);

  const [, forceUpdate] = useReducer(x => x + 1, 0);

  function handleClick() {
    countref.current++;
    console.log("Count = ", countref.current);
    forceUpdate(); // If you comment this out, the date and count in the screen will not be updated
  }

  return (
    <div className="App">
      <h1> {new Date().toLocaleString()} </h1>
      <h2>You clicked {countref.current} times</h2>
      <button
        onClick={() => {
          handleClick();
        }}
      >
        ClickToUpdateDateAndCount
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

참고 : useReducer 대신 useState 후크를 사용하는 대체 방법 도이 URL 에서 사용할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.