programing

컴포넌트 상태를 직접 변경할 수 없는 이유는 무엇입니까?

stoneblock 2023. 3. 20. 21:22

컴포넌트 상태를 직접 변경할 수 없는 이유는 무엇입니까?

리액트 튜토리얼과 문서는 상태를 직접 변환해서는 안 되며 모든 것을 거쳐야 한다고 경고하는 것을 알고 있습니다.setState.

알고 .this.setState({}), 「」를 하기 .render.

예: 다음 코드는 정상적으로 동작하는 것 같습니다.

const React = require('react');

const App = React.createClass({
  getInitialState: function() {
    return {
      some: {
        rather: {
          deeply: {
            embedded: {
              stuff: 1,
            },
          },
        },
      },
    },
  };
  updateCounter: function () {
    this.state.some.rather.deeply.embedded.stuff++;
    this.setState({}); // just to trigger the render ...
  },
  render: function() {
    return (
      <div>
        Counter value: {this.state.some.rather.deeply.embedded.stuff}
        <br></br>
        <button onClick={this.updateCounter}>Increment</button>
      </div>
    );
  },
});

export default App;

저는 규약을 따르는 것에 찬성합니다만, React에 대한 이해를 높이고 싶습니다.JS는 실제로 동작하며 무엇이 잘못될 수 있는지, 또는 위의 코드에서는 최적이라고 할 수 없는지를 나타냅니다.

문서의 주에서는 기본적으로 다음 두 가지 gotcha를 식별합니다.

  1. 하고 나서, 그 에 콜을 발신했을 .this.setState이것은 당신이 만든 돌연변이를 대체할 수 있습니다.위의 코드에서는 어떻게 이런 일이 일어날 수 있는지 모르겠습니다.
  2. ★★★setState를 일으킬 수 this.state 등의 으로 액세스 할 수 .this.state this.setState최종 변환 상태에 액세스 할 수 있는 것은 아닙니다.가 안 거구나.this.setState는 업데이트 기능의 마지막 호출입니다.

이 답변은 React에서 직접 상태를 변경/변환하지 않도록 충분한 정보를 제공하는 것입니다.

리액션은 단방향 데이터 흐름에 따릅니다.즉, 반응 내부의 데이터 흐름은 순환 경로여야 하며, 순환 경로여야 합니다.

플럭스 없이 반응하는 데이터 흐름

여기에 이미지 설명 입력

React를 이렇게 작동시키기 위해 개발자들은 React를 기능 프로그래밍과 유사하게 만들었습니다.기능 프로그래밍의 경험칙은 불변성이다.제가 크고 명확하게 설명해 드릴게요.

단방향 흐름은 어떻게 작동합니까?

  • states는 컴포넌트의 데이터를 포함하는 데이터스토어입니다.
  • view컴포넌트 렌더링의 경우 상태를 기준으로 합니다.
  • ?view가 있기 그 은 '바꾸다'에서되어야 합니다.store.
  • 하기 위해 는 "React"를 제공합니다.setState() function which which an which which object(新)의states 및 )object.assign()이전 상태를 덮어쓰고 새 상태를 상태 데이터스토어에 추가합니다.
  • 변경될 는 새로운 상태의 이는 '리액트'가 됩니다.view는 그것을 소비하여 화면에 표시합니다.

이 주기는 구성 요소의 수명 동안 계속됩니다.

위의 단계를 보면 상태를 바꿀 때 뒤에서 많은 일이 일어나고 있음을 알 수 있습니다.하여 콜을 할 때setState()을 사용하다previous state의 스테이트 밖에 때문입니다.이로 인해 두 상태의 얕은 비교 및 병합이 방해되거나 발생하지 않습니다. 왜냐하면 이제 하나의 상태만 갖게 되기 때문입니다.이로 인해 React의 라이프 사이클 방법이 모두 중단됩니다.

그 결과, 앱이 비정상적으로 동작하거나 크래쉬 할 수도 있습니다.테스트에 사용하는 앱은 모두 매우 작기 때문에 대부분의 경우 앱에 영향을 주지 않습니다.

그리고 돌연변이의 또 다른 단점은Objects ★★★★★★★★★★★★★★★★★」ArraysJavaScript에서는 객체 또는 배열을 할당할 때 해당 객체 또는 배열을 참조하는 것입니다.변환하면 해당 개체 또는 어레이에 대한 모든 참조가 영향을 받습니다.React는 이것을 백그라운드에서 인텔리전트하게 처리하며 단순히 동작시키기 위한 API를 제공합니다.

React에서 상태를 처리할 때 발생하는 가장 일반적인 오류

// original state
this.state = {
  a: [1,2,3,4,5]
}

// changing the state in react
// need to add '6' in the array

// bad approach
const b = this.state.a.push(6)
this.setState({
  a: b
}) 

예에서는 " " " 입니다.this.state.a.push(6)상태를 직접 변환합니다. 하고 호출하다setState아래 그림과 같습니다.하여 호출하는.setState그 변수와 함께.

// same as 
this.state.a.push(6)
this.setState({})

많은 사람들이 이렇게 해요.이건 너무 잘못됐어.이것은 리액트의 장점을 깨뜨리고 프로그래밍 연습에 나쁜 영향을 미칩니다.

그럼, 리액트의 상태를 처리하는 가장 좋은 방법은 무엇일까요?제가 설명해 드릴게요.

기존 상태에서 'something'을 변경해야 할 경우 먼저 현재 상태에서 해당 'something' 복사본을 가져옵니다.

// original state
this.state = {
  a: [1,2,3,4,5]
}

// changing the state in react
// need to add '6' in the array

// create a copy of this.state.a
// you can use ES6's destructuring or loadash's _.clone()
const currentStateCopy = [...this.state.a]

그럼 이제 ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, 음.currentStateCopy원래 상태를 변형시키지 않습니다.을 하다currentStateCopy 해서 새로운 해 주세요.setState().

currentStateCopy.push(6)
this.setState({
  a: currentStateCopy
})

아름답죠?

는 '''의 참조가 됩니다'''this.state.a 않을 것이다.setState이를 통해 코드를 제어할 수 있으며 우아한 테스트를 작성할 수 있으며 프로덕션에서 코드의 성능을 확신할 수 있습니다.

당신의 질문에 답하기 위해서

컴포넌트 상태를 직접 변경할 수 없는 이유는 무엇입니까?


글쎄, 할 수 있어.하지만 다음과 같은 결과를 맞이해야 합니다.

  1. 규모를 확장하면 관리하기 어려운 코드를 쓰게 됩니다.
  2. 됩니다.state컴포넌트 전체에 걸쳐 있습니다.
  3. React를 사용하는 대신 React 위에 커스텀 코드를 작성합니다.

JavaScript는 싱글 스레드이기 때문에 불변성은 필수는 아니지만 장기적으로는 도움이 되는 방법을 따르는 것이 좋습니다.

추신. 저는 변이 가능한 리액트 JS 코드를 10,000줄 정도 작성했습니다.지금 깨지면 어디선가 모든 값이 변형되어 있기 때문에 어디를 봐야 할지 모르겠습니다.이것을 깨달았을 때, 나는 불변의 코드를 쓰기 시작했다.날 믿어!그것이 제품이나 앱에 할 수 있는 최선의 방법입니다.

의 React 문서에는 다음과 같이 기술되어 있습니다.

변이하지 않다this.state 「」, 「」를 발신하는 setState()나중에 당신이 만든 돌연변이를 대체할 수 있습니다.this.state마치 불변의 존재인 것처럼요.

setState()는 즉시 .this.state을 사용법" " 의 액세스this.state이 메서드를 호출하면 기존 값이 반환될 수 있습니다.

「」에의 .setState퍼포먼스 향상을 위해 콜을 배치하는 경우가 있습니다.

setState()는 조건부 되지 않는 한 항상 .shouldComponentUpdate(). 객체가 할 수 경우변환 가능한 오브젝트가 사용되고 있으며 로직을 구현할 수 없는 경우shouldComponentUpdate(), 호출, 호출setState()새로운 상태가 이전 상태와 다른 경우에만 불필요한 재검출을 피할 수 있습니다.

「」를 는,this.state이러한 수정 사항을 직접 덮어쓸 수 있는 상황을 만듭니다.

1) 2)에 대하여 1) 2)에 대하여setState()즉시는 아닙니다.에 대한 직접 변경을 포함하지 않을 수 있다고 생각되는 상태에 따라 큐잉합니다.즉시 적용되지 않고 큐잉되기 때문에 직접 변경을 덮어쓸 수 있도록 그 사이에 변경이 있을 가능성이 있습니다.

외에는 직접 하지 않는 .this.state좋은 관행이라고 볼 수 있습니다.코드가 React와 상호 작용하여 이러한 덮어쓰기나 다른 문제가 발생하지 않음을 개인적으로 알고 있을 수 있지만, 다른 개발자 또는 향후 업데이트에서 갑자기 이상하거나 미묘한 문제가 발생할 수 있습니다.

에 대한 가장 간단한 대답

컴포넌트 상태를 직접 변경할 수 없는 이유:

업데이트 단계와 관련된 것입니다.

컴포넌트의 상태를 갱신하면, 그 모든 컴포넌트의 아이도 렌더링 됩니다.또는 전체 컴포넌트 트리를 렌더링합니다.

그러나 컴포넌트 트리 전체가 렌더링되었다고 해서 DOM 전체가 갱신되는 것은 아닙니다.컴포넌트가 렌더링되면 기본적으로 반응 요소가 생성됩니다.그것이 가상 돔을 갱신하는 것입니다.

그런 다음 React는 가상 DOM을 조사합니다.또한 오래된 가상 DOM의 복사본도 가지고 있기 때문에 상태를 직접 업데이트하지 않도록 해야 합니다.따라서 메모리에는 오래된 가상 DOM과 새로운 가상 DOM이 있습니다.

그러면 react는 무엇이 변경되었는지 파악하고 그에 따라 실제 DOM을 업데이트합니다.

도움이 됐으면 좋겠네요

현재 답변 중 순수/메모 컴포넌트(React.PureComponent 또는 )에 대해 언급하지 않은 것이 놀랍습니다.이러한 컴포넌트는 소품 중 하나의 변경이 검출되었을 경우에만 재렌더됩니다.

상태를 직접 변환하고 값이 아니라 아래 컴포넌트에 오버커플링 개체를 전달합니다.이 개체는 이전 개체와 동일한 참조를 가지고 있습니다. 즉, 속성 중 하나를 변환했더라도 순수/메모 구성 요소가 다시 렌더링되지 않습니다.

라이브러리에서 컴포넌트를 Import할 때 어떤 유형의 컴포넌트를 사용하고 있는지 항상 알 수 있는 것은 아니기 때문에, 이것은 변환하지 않는 규칙을 고수할 필요가 있는 또 다른 이유입니다.

다음은 이 동작의 예입니다(복사 작성 및 중첩된 콘텐츠 업데이트를 단순화하기 위해 사용).

class App extends React.Component {
  state = { some: { rather: { deeply: { nested: { stuff: 1 } } } } };
  
  mutatingIncrement = () => {
    this.state.some.rather.deeply.nested.stuff++;
    this.setState({});
  }
  nonMutatingIncrement = () => {
    this.setState(R.evolve(
      { some: { rather: { deeply: { nested: { stuff: n => n + 1 } } } } }
    ));
  }

  render() {
    return (
      <div>
        Normal Component: <CounterDisplay {...this.state} />
        <br />
        Pure Component: <PureCounterDisplay {...this.state} />
        <br />
        <button onClick={this.mutatingIncrement}>mutating increment</button>
        <button onClick={this.nonMutatingIncrement}>non-mutating increment</button>
      </div>
    );
  }
}

const CounterDisplay = (props) => (
  <React.Fragment>
    Counter value: {props.some.rather.deeply.nested.stuff}
  </React.Fragment>
);
const PureCounterDisplay = React.memo(CounterDisplay);

ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/ramda@0/dist/ramda.min.js"></script>
<div id="root"></div>

""의 않도록 하기 위해this.state.element업데이트를 사용할 수 있습니다.$set or $push또는 불변으로 인한 다른 많은 것들도

예:

import update from 'immutability-helper';

const newData = update(myData, {
  x: {y: {z: {$set: 7}}},
  a: {b: {$push: [9]}}
});

setState는 컴포넌트의 렌더링을 트리거합니다.상태를 계속 업데이트하려면 setState를 설정해야 합니다.그렇지 않으면 올바르게 동작하지 않습니다.

제가 현재 이해하고 있는 내용다음과 같습니다.

사용하지 않는 경우 shouldComponentUpdate 다른 방식(예: '다' 등)을 사용합니다.componentWillReceiveProps,componentWillUpdate , , , , 입니다.componentDidUpdate) 합니다.)props/state

그리고나서

state에 전화해요.setState()그렇지 않으면 괜찮지 않습니다.

언급URL : https://stackoverflow.com/questions/37755997/why-cant-i-directly-modify-a-components-state-really