programing

반응 – 폼 요소 상태를 형제/부모 요소에 전달하는 올바른 방법?

cafebook 2023. 3. 9. 22:20
반응형

반응 – 폼 요소 상태를 형제/부모 요소에 전달하는 올바른 방법?

  • C1과 C2의 두 가지 자식 클래스를 렌더링하는 React 클래스 P가 있다고 가정합니다.
  • C1에는 입력 필드가 포함되어 있습니다.이 입력 필드를 Foo라고 합니다.
  • 내 목표는 C2가 Foo의 변화에 반응하도록 하는 것이다.

두 가지 해결책이 떠올랐는데 둘 다 옳지 않다고 느껴요.

첫 번째 솔루션:

  1. P로 합니다.state.input.
  2. 작성하다onChange: "P"를 설정합니다.state.input.
  3. 을 건네주세요.onChange을 C1로 props을 C1로 바인드 합니다.this.props.onChangeonChange★★★★★★ 。

이거 되는구나.변경될 가 .setState따라서 P는 C2에 전달하기 위한 입력을 갖게 됩니다.

하지만 같은 이유로 그다지 옳지 않은 것 같습니다.자식 요소에서 부모 요소의 상태를 설정하는 중입니다.이는 단방향 데이터 흐름이라는 리액트의 설계 원칙을 위반하는 것으로 보입니다.
래래이 렇렇 ?? ??? 아니면 더 자연스러운 리액트 솔루션이 있나요?

두 번째 솔루션:

푸를 P에 넣어주세요.

그러나 앱을 구성할 때 따라야 하는 디자인 원칙은 무엇입니까? 모든 폼 요소를render장장 높은 등??

큰 C1의 는 정말 .render ~ C1 ~ ~renderC1 c c 、 C1 c c 、 P1 c c c c 。

어떻게 하면 좋을까요?

제가 제대로 이해하고 있다면, 첫 번째 솔루션은 루트 컴포넌트에서 상태를 유지하는 것입니다.리액트의 크리에이터를 대변할 수는 없지만, 일반적으로는 이것이 적절한 해결책이라고 생각합니다.

상태를 유지하는 것이 React가 만들어진 이유 중 하나입니다.상호의존적인 이동 부분이 많은 동적 UI를 처리하기 위해 자체 상태 패턴 클라이언트 측을 구현한 적이 있다면 React가 좋습니다. 상태 관리의 많은 어려움을 덜어주기 때문입니다.

계층에서 상태를 더 위쪽으로 유지하고 이벤트를 통해 업데이트함으로써 데이터 흐름은 여전히 거의 단방향입니다. Root 컴포넌트의 이벤트에 응답할 뿐이지 실제로는 양방향 바인딩을 통해 데이터를 가져올 수 없습니다. Root 컴포넌트에 "여기서 무슨 일이 일어났어요. 값을 확인하세요." 또는상태를 업데이트하기 위해 하위 구성요소의 일부 데이터 상태를 위로 전달합니다.C1에서 상태를 변경하여 C2가 이를 인식하도록 했습니다.따라서 루트 컴포넌트에서 상태를 갱신하고 재렌더링함으로써 루트 컴포넌트에서 상태가 갱신되어 전달된 이후 C2의 프로포트가 동기화됩니다.

class Example extends React.Component {
  constructor (props) {
    super(props)
    this.state = { data: 'test' }
  }
  render () {
    return (
      <div>
        <C1 onUpdate={this.onUpdate.bind(this)}/>
        <C2 data={this.state.data}/>
      </div>
    )
  }
  onUpdate (data) { this.setState({ data }) }
}

class C1 extends React.Component {
    render () {
      return (
        <div>
          <input type='text' ref='myInput'/>
          <input type='button' onClick={this.update.bind(this)} value='Update C2'/>
        </div>
      )
    }
    update () {
      this.props.onUpdate(this.refs.myInput.getDOMNode().value)
    }
})

class C2 extends React.Component {
    render () {
      return <div>{this.props.data}</div>
    }
})

ReactDOM.renderComponent(<Example/>, document.body)

React를 사용하여 앱을 만든 후 반년 전에 했던 질문에 대해 몇 가지 생각을 나누고 싶습니다.

꼭 읽어보시길 권합니다.

첫 번째 글은 리액트 앱의 구조를 이해하는 데 매우 도움이 됩니다.

Flux는 React 앱을 구조화하는 방법이 아닌 이러한 방식으로 구성해야 하는 이유에 대해 답변합니다.React는 시스템의 50%에 불과하며, Flux를 사용하면 전체 그림을 볼 수 있으며 어떻게 일관된 시스템을 구성하는지 확인할 수 있습니다.

다시 질문으로 돌아가자.

첫 번째 해결책입니다만, 데이터는 아직 단방향이기 때문에 핸들러를 역방향으로 해도 괜찮습니다.

다만, 핸들러가 P 로 setState 를 트리거 하도록 하는 것은, 상황에 따라서는 옳거나 그른 경우가 있습니다.

만약 앱이 단순한 Markdown 컨버터이고, C1이 원시 입력이고, C2가 HTML 출력이라면, C1이 P에서 setState를 트리거하도록 해도 괜찮지만, 일부는 이것이 권장되지 않는 방법이라고 주장할 수 있다.

목록인 하는 .dispatcher , 、 , , 、store 갱신하다data store그런 다음 데이터를 P로 전송하고 보기를 채웁니다.플럭스 기사를 참조하십시오.예: 플럭스 - TodoMVC

일반적으로, 저는 ToDo 목록 예제에 설명된 방식을 선호합니다.앱 상태가 적을수록 좋습니다.

5년 후 React Hooks가 등장하면서 use Context hook을 사용하여 훨씬 우아한 방법을 사용할 수 있게 되었습니다.

글로벌 범위에서 컨텍스트를 정의하고 상위 구성 요소에서 변수, 개체 및 함수를 내보낸 다음 App에서 제공된 컨텍스트로 하위 구성 요소를 래핑하고 하위 구성 요소에 필요한 모든 항목을 가져옵니다.다음은 개념 증명입니다.

import React, { useState, useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.css";

// Create context container in a global scope so it can be visible by every component
const ContextContainer = React.createContext(null);

const initialAppState = {
  selected: "Nothing"
};

function App() {
  // The app has a state variable and update handler
  const [appState, updateAppState] = useState(initialAppState);

  return (
    <div>
      <h1>Passing state between components</h1>

      {/* 
          This is a context provider. We wrap in it any children that might want to access
          App's variables.
          In 'value' you can pass as many objects, functions as you want. 
           We wanna share appState and its handler with child components,           
       */}
      <ContextContainer.Provider value={{ appState, updateAppState }}>
        {/* Here we load some child components */}
        <Book title="GoT" price="10" />
        <DebugNotice />
      </ContextContainer.Provider>
    </div>
  );
}

// Child component Book
function Book(props) {
  // Inside the child component you can import whatever the context provider allows.
  // Earlier we passed value={{ appState, updateAppState }}
  // In this child we need the appState and the update handler
  const { appState, updateAppState } = useContext(ContextContainer);

  function handleCommentChange(e) {
    //Here on button click we call updateAppState as we would normally do in the App
    // It adds/updates comment property with input value to the appState
    updateAppState({ ...appState, comment: e.target.value });
  }

  return (
    <div className="book">
      <h2>{props.title}</h2>
      <p>${props.price}</p>
      <input
        type="text"
        //Controlled Component. Value is reverse vound the value of the variable in state
        value={appState.comment}
        onChange={handleCommentChange}
      />
      <br />
      <button
        type="button"
        // Here on button click we call updateAppState as we would normally do in the app
        onClick={() => updateAppState({ ...appState, selected: props.title })}
      >
        Select This Book
      </button>
    </div>
  );
}

// Just another child component
function DebugNotice() {
  // Inside the child component you can import whatever the context provider allows.
  // Earlier we passed value={{ appState, updateAppState }}
  // but in this child we only need the appState to display its value
  const { appState } = useContext(ContextContainer);

  /* Here we pretty print the current state of the appState  */
  return (
    <div className="state">
      <h2>appState</h2>
      <pre>{JSON.stringify(appState, null, 2)}</pre>
    </div>
  );
}

const rootElement = document.body;
ReactDOM.render(<App />, rootElement);

이 예는 Code Sandbox 편집기에서 실행할 수 있습니다.

컨텍스트를 사용한 통과 상태 편집

상태를 부모 컴포넌트로 유지하는 첫 번째 솔루션이 올바른 해결책입니다.그러나 보다 복잡한 문제의 경우 상태 관리 라이브러리를 고려해야 합니다. redux는 react와 함께 사용되는 가장 일반적인 라이브러리입니다.

글을 쓰고 있는 지금, 직설적인 리액트 솔루션에 대한 답이 없는 것에 놀랐습니다.예를 들어 다음과 같습니다(다른 제품과 크기와 복잡성을 비교).

class P extends React.Component {
    state = { foo : "" };

    render(){
        const { foo } = this.state;

        return (
            <div>
                <C1 value={ foo } onChange={ x => this.setState({ foo : x })} />
                <C2 value={ foo } />
            </div>
        )
    }
}

const C1 = ({ value, onChange }) => (
    <input type="text"
           value={ value }
           onChange={ e => onChange( e.target.value ) } />
);

const C2 = ({ value }) => (
    <div>Reacting on value change: { value }</div>
);

자식 요소에서 부모 요소의 상태를 설정하는 중입니다.이는 단방향 데이터 흐름이라는 리액트의 설계 원칙을 위반하는 것으로 보입니다.

제어된 (React에서 폼을 사용하는 자동적인 방법)는 부모 상태를 업데이트합니다.onChange콜백하고도 아무것도 배신하지 않아요

예를 들어 C1 컴포넌트를 주의 깊게 살펴보십시오.어떻게 하면 중요한 차이가 보이나요?C1내장input컴포넌트가 상태 변경을 처리합니까?그러면 안 돼요, 없어요.상태를 올리고 값/onChange 쌍을 전달하는 것은 raw React에 대한 관용입니다.일부 답변에서 알 수 있듯이 참조 용도가 아닙니다.

예를 들어 다음과 같은 최신 답변을 제시합니다.React.useState

부모 컴포넌트의 상태를 유지하는 것이 좋습니다.부모가 2개의 자식 구성 요소에서 관리하므로 부모에게 액세스 권한이 있어야 합니다.소프트웨어 엔지니어링에서 글로벌 변수가 일반적으로 로컬 변수보다 나쁜 것과 같은 이유로 Redux에서 관리하는 변수와 같이 글로벌 상태로 이행하는 은 권장되지 않습니다.

상태가 부모 컴포넌트에 있는 경우 부모가 자녀에게 준 경우 자녀는 이를 변환할 수 있습니다.value그리고.onChange소품(값 링크 또는 상태 링크 패턴이라고도 함)의 핸들러입니다.후크를 사용하는 방법은 다음과 같습니다.


function Parent() {
    var [state, setState] = React.useState('initial input value');
    return <>
        <Child1 value={state} onChange={(v) => setState(v)} />
        <Child2 value={state}>
    </>
}

function Child1(props) {
    return <input
        value={props.value}
        onChange={e => props.onChange(e.target.value)}
    />
}

function Child2(props) {
    return <p>Content of the state {props.value}</p>
}

상위 구성 요소 전체가 하위 구성 요소의 입력 변경에 따라 다시 렌더링됩니다. 상위 구성 요소가 작거나 다시 렌더링하는 속도가 빠르면 문제가 되지 않을 수 있습니다.부모 컴포넌트의 재렌더 퍼포먼스는 일반적인 경우에서도 문제가 될 수 있습니다(예를 들어 큰 형식).이 문제는 고객님의 경우 해결되었습니다(아래 참조).

상태 링크 패턴 및 부모 리렌더 없음은 Hookstate - supercharge와 같은 서드파티 라이브러리를 사용하여 구현하기 쉽습니다.React.useState고객의 사례를 포함한 다양한 사용 사례를 커버합니다.(해임자:저는 이 프로젝트의 작성자입니다).

후크스테이트 Child1.Child2반응할 거예요. Parent변경 시단, '상태 변경 시 재실행되지 않습니다.Child1 ★★★★★★★★★★★★★★★★★」Child2contracty underbanda 。

import { useStateLink } from '@hookstate/core';

function Parent() {
    var state = useStateLink('initial input value');
    return <>
        <Child1 state={state} />
        <Child2 state={state}>
    </>
}

function Child1(props) {
    // to avoid parent re-render use local state,
    // could use `props.state` instead of `state` below instead
    var state = useStateLink(props.state)
    return <input
        value={state.get()}
        onChange={e => state.set(e.target.value)}
    />
}

function Child2(props) {
    // to avoid parent re-render use local state,
    // could use `props.state` instead of `state` below instead
    var state = useStateLink(props.state)
    return <p>Content of the state {state.get()}</p>
}

PS: 여기에서는, 보다 복잡한 유사 시나리오에 대해 설명하고 있습니다.예를 들어, 깊이 중첩된 데이터, 스테이트 검증, 글로벌 상태, 및setState훅 등Hookstate와 위에서 설명한 기술을 사용하는 완전한 샘플 어플리케이션도 온라인에 있습니다.

Redux 및 ReactRedux 라이브러리를 익혀야 합니다.이것은 당신의 상태와 소품들을 하나의 스토어에 정리할 것이고 당신은 나중에 당신의 컴포넌트에서 그것들을 이용할 수 있을 것이다.

반응 > = 16.3「forwardRef」는, 「DOM」의 「DOM」의 「ForwardRef」를 참조합니다.더 이상 옛날식 레퍼런스를 쓰지 마세요.
다음과 .

import React, { Component } from 'react';

export default class P extends React.Component {
   constructor (props) {
      super(props)
      this.state = {data: 'test' }
      this.onUpdate = this.onUpdate.bind(this)
      this.ref = React.createRef();
   }

   onUpdate(data) {
      this.setState({data : this.ref.current.value}) 
   }

   render () {
      return (
        <div>
           <C1 ref={this.ref} onUpdate={this.onUpdate}/>
           <C2 data={this.state.data}/>
        </div>
      )
   }
}

const C1 = React.forwardRef((props, ref) => (
    <div>
        <input type='text' ref={ref} onChange={props.onUpdate} />
    </div>
));

class C2 extends React.Component {
    render () {
       return <div>C2 reacts : {this.props.data}</div>
    }
}

참조 및 ForwardRef에 대한 자세한 내용은 참조 및 ForwardRef참조하십시오.

  1. 올바른 방법은 부모 컴포넌트에 상태를 유지하고 참조를 회피하는 것입니다.
  2. 문제는 필드에 입력할 때 모든 하위 항목을 지속적으로 업데이트하지 않도록 하는 것입니다.
  3. 각 는 (Component가 컴포넌트가 'Pure Component'(Pure Component)를 구현해야 .shouldComponentUpdate(nextProps, nextState)
  4. 이렇게 하면 양식 필드에 입력할 때 해당 필드만 업데이트됩니다.

는 '먹다'를 사용해요.@boundES의 주석입니다.다음 babel-plugin-transform-decorators-legacy바벨의JS 6 및 클래스 속성(주석은 바인딩과 유사한 멤버 함수에 이 값을 설정합니다):

/*
© 2017-present Harald Rudell <harald.rudell@gmail.com> (http://www.haraldrudell.com)
All rights reserved.
*/
import React, {Component} from 'react'
import {bound} from 'class-bind'

const m = 'Form'

export default class Parent extends Component {
  state = {one: 'One', two: 'Two'}

  @bound submit(e) {
    e.preventDefault()
    const values = {...this.state}
    console.log(`${m}.submit:`, values)
  }

  @bound fieldUpdate({name, value}) {
    this.setState({[name]: value})
  }

  render() {
    console.log(`${m}.render`)
    const {state, fieldUpdate, submit} = this
    const p = {fieldUpdate}
    return (
      <form onSubmit={submit}> {/* loop removed for clarity */}
        <Child name='one' value={state.one} {...p} />
        <Child name='two' value={state.two} {...p} />
        <input type="submit" />
      </form>
    )
  }
}

class Child extends Component {
  value = this.props.value

  @bound update(e) {
    const {value} = e.target
    const {name, fieldUpdate} = this.props
    fieldUpdate({name, value})
  }

  shouldComponentUpdate(nextProps) {
    const {value} = nextProps
    const doRender = value !== this.value
    if (doRender) this.value = value
    return doRender
  }

  render() {
    console.log(`Child${this.props.name}.render`)
    const {value} = this.props
    const p = {value}
    return <input {...p} onChange={this.update} />
  }
}

부모에서 자녀로 데이터를 전달하는 개념과 그 반대 개념에 대해 설명합니다.

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

// taken refrence from https://gist.github.com/sebkouba/a5ac75153ef8d8827b98

//example to show how to send value between parent and child

//  props is the data which is passed to the child component from the parent component

class Parent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      fieldVal: ""
    };
  }

  onUpdateParent = val => {
    this.setState({
      fieldVal: val
    });
  };

  render() {
    return (
      // To achieve the child-parent communication, we can send a function
      // as a Prop to the child component. This function should do whatever
      // it needs to in the component e.g change the state of some property.
      //we are passing the function onUpdateParent to the child
      <div>
        <h2>Parent</h2>
        Value in Parent Component State: {this.state.fieldVal}
        <br />
        <Child onUpdate={this.onUpdateParent} />
        <br />
        <OtherChild passedVal={this.state.fieldVal} />
      </div>
    );
  }
}

class Child extends Component {
  constructor(props) {
    super(props);

    this.state = {
      fieldValChild: ""
    };
  }

  updateValues = e => {
    console.log(e.target.value);
    this.props.onUpdate(e.target.value);
    // onUpdateParent would be passed here and would result
    // into onUpdateParent(e.target.value) as it will replace this.props.onUpdate
    //with itself.
    this.setState({ fieldValChild: e.target.value });
  };

  render() {
    return (
      <div>
        <h4>Child</h4>
        <input
          type="text"
          placeholder="type here"
          onChange={this.updateValues}
          value={this.state.fieldVal}
        />
      </div>
    );
  }
}

class OtherChild extends Component {
  render() {
    return (
      <div>
        <h4>OtherChild</h4>
        Value in OtherChild Props: {this.props.passedVal}
        <h5>
          the child can directly get the passed value from parent by this.props{" "}
        </h5>
      </div>
    );
  }
}

ReactDOM.render(<Parent />, document.getElementById("root"));

언급URL : https://stackoverflow.com/questions/24147331/react-the-right-way-to-pass-form-element-state-to-sibling-parent-elements

반응형