React에서 자녀의 상태에 액세스하는 방법은 무엇입니까?


215

나는 다음과 같은 구조를 가지고 있습니다 :

FormEditor-여러 FieldEditor를 보유 FieldEditor-양식의 필드를 편집하고 상태에 대한 다양한 값을 저장합니다.

FormEditor 내에서 단추를 클릭하면 모든 FieldEditor구성 요소 에서 필드에 대한 정보 , 해당 상태 에있는 정보를 수집하여 FormEditor 내에 모두 포함시킬 수 있기를 원합니다.

나는 FieldEditor상태 외부의 필드에 대한 정보를 저장하는 FormEditor대신에 상태를 저장하는 것을 고려 했습니다 . 그러나 정보를 변경하고 상태로 저장 FormEditor하면 각 FieldEditor구성 요소 를 청취 해야 합니다 .

대신 어린이의 상태에 접근 할 수 없습니까? 이상적입니까?


4
"대신 어린이의 상태에 접근 할 수 없습니까? 이상적인가요?" 아닙니다. 상태는 내부적 인 것이며 외부로 누출되어서는 안됩니다. 구성 요소에 대한 접근 자 메서드를 구현할 수 있지만 이상적이지는 않습니다.
Felix Kling

@FelixKling 그렇다면 자녀와 부모의 의사 소통을위한 이상적인 방법은 이벤트 일 뿐이라고 제안하십니까?
Moshe Revah

3
예, 이벤트는 한 가지 방법입니다. 또는 Flux가 홍보하는 것과 같은 하나의 방향성 데이터 흐름이 있습니다. facebook.github.io/flux
Felix Kling

1
FieldEditors를 별도로 사용하지 않을 경우 상태를 FormEditor양호하게 저장하십시오 . 이 경우 FieldEditor인스턴스는 props자신의 폼 편집기가 아닌 폼 편집기 에서 전달 한대로 렌더링 합니다 state. 더 복잡하지만 유연한 방법은 컨테이너 하위를 통과하는 시리얼 라이저를 만들고 FormEditor그중의 모든 인스턴스를 찾아 JSON 객체로 직렬화하는 것입니다. JSON 오브젝트는 양식 편집기에서 인스턴스의 중첩 레벨을 기반으로 선택적으로 중첩 될 수 있습니다 (하나 이상의 레벨).
Meglio

4
나는 React Docs의 'Lifting State Up' 이 아마도 이것을하는 가장 'Reacty'방식 이라고 생각합니다
icc97

답변:


136

개별 FieldEditors에 대한 onChange 핸들러가 이미있는 경우 상태를 FormEditor 구성 요소로 옮기지 않고 콜백을 거기에서 상위 상태를 업데이트하는 FieldEditors로 전달하는 이유를 알 수 없습니다. 그것은 더 반응적인 방법으로 보입니다.

아마도 이것의 라인을 따라 뭔가 :

const FieldEditor = ({ value, onChange, id }) => {
  const handleChange = event => {
    const text = event.target.value;
    onChange(id, text);
  };

  return (
    <div className="field-editor">
      <input onChange={handleChange} value={value} />
    </div>
  );
};

const FormEditor = props => {
  const [values, setValues] = useState({});
  const handleFieldChange = (fieldId, value) => {
    setValues({ ...values, [fieldId]: value });
  };

  const fields = props.fields.map(field => (
    <FieldEditor
      key={field}
      id={field}
      onChange={handleFieldChange}
      value={values[field]}
    />
  ));

  return (
    <div>
      {fields}
      <pre>{JSON.stringify(values, null, 2)}</pre>
    </div>
  );
};

// To add abillity to dynamically add/remove fields keep the list in state
const App = () => {
  const fields = ["field1", "field2", "anotherField"];

  return <FormEditor fields={fields} />;
};

원본-사전 후크 버전 :


2
이것은 onChange에 유용하지만 onSubmit에는 그다지 유용하지 않습니다.
190290000 루블 맨

1
@ 190290000RubleMan 무슨 말인지 모르겠지만 개별 필드의 onChange 핸들러는 FormEditor 구성 요소의 상태에서 항상 완전한 양식 데이터를 사용할 수 있으므로 onSubmit은 다음과 같은 것을 포함합니다 myAPI.SaveStuff(this.state);. 좀 더 정교하게 작성하면 더 나은 답변을 드릴 수 있습니다. :)
Markus-ipse

각각 다른 유형의 3 개의 필드가 있고 각각 고유 한 유효성 검사가있는 양식이 있다고 가정합니다. 제출하기 전에 각각을 확인하고 싶습니다. 유효성 검사에 실패하면 오류가있는 각 필드를 다시 렌더링해야합니다. 제안 된 솔루션 으로이 작업을 수행하는 방법을 알지 못했지만 방법이있을 수 있습니다!
190290000 루블 맨

1
@ 190290000RubleMan 원래 질문과 다른 경우처럼 보입니다. 자세한 내용과 샘플 코드가 포함 된 새 질문을 열면 나중에 살펴볼 수 있습니다. :)
Markus-ipse

이 답변이 유용하다는 것을 알 수 있습니다 . 상태 후크를 사용하고, 상태를 생성해야하는 위치, 모든 키 입력에서 폼을 다시 렌더링하지 않는 방법, 필드 유효성 검사를 수행하는 방법 등을 설명합니다.
Andrew

189

하위 구성 요소의 상태에 액세스하는 방법에 대해 자세히 알아보기 전에이 특정 시나리오를 처리하는 더 나은 솔루션에 대한 Markus-ipse 의 답변 을 읽으십시오 .

실제로 구성 요소의 자식 상태에 액세스하려는 경우 ref각 자식에게 호출되는 속성을 할당 할 수 있습니다 . 참조를 구현하는 두 가지 방법이 있습니다. 사용 React.createRef()및 콜백 참조 사용 .

사용 React.createRef()

현재 React 16.3에서 참조를 사용하는 것이 권장되는 방법입니다 (자세한 내용 은 문서 참조 ). 이전 버전을 사용하는 경우 콜백 참조와 관련하여 아래를 참조하십시오.

부모 구성 요소의 생성자에서 새 참조를 만든 다음 ref속성을 통해 자식에 할당해야 합니다.

class FormEditor extends React.Component {
  constructor(props) {
    super(props);
    this.FieldEditor1 = React.createRef();
  }
  render() {
    return <FieldEditor ref={this.FieldEditor1} />;
  }
}

이런 종류의 심판에 액세스하려면 다음을 사용해야합니다.

const currentFieldEditor1 = this.FieldEditor1.current;

그러면 마운트 된 구성 요소의 인스턴스가 반환되므로 currentFieldEditor1.state상태에 액세스하는 데 사용할 수 있습니다.

그냥 빨리 메모 대신 구성 요소의 DOM 노드에서 이러한 참조를 사용하는 경우 (예를 들어 있다고합니다 <div ref={this.divRef} />) 다음 this.divRef.current구성 요소 인스턴스 대신 기본 DOM 요소를 반환합니다.

콜백 참조

이 속성은 연결된 구성 요소에 대한 참조로 전달되는 콜백 함수를 사용합니다. 이 콜백은 구성 요소가 마운트 또는 마운트 해제 된 직후에 실행됩니다.

예를 들면 다음과 같습니다.

<FieldEditor
    ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
    {...props}
/>

이 예제에서 참조는 상위 구성 요소에 저장됩니다. 코드에서이 컴포넌트를 호출하려면 다음을 사용할 수 있습니다.

this.fieldEditor1

그런 다음 this.fieldEditor1.state상태를 얻는 데 사용 합니다.

참고로 자식 구성 요소에 액세스하기 전에 렌더링해야합니다. ^ _ ^

대신 구성 요소의 DOM 노드에서 이러한 참조를 사용하는 경우 위와 같이, (예 <div ref={(divRef) => {this.myDiv = divRef;}} />) 다음 this.divRef구성 요소 인스턴스 대신 기본 DOM 요소를 반환합니다.

추가 정보

React의 ref 속성에 대한 자세한 내용을 보려면 Facebook 에서이 페이지 를 확인 하십시오 .

자녀 가 "일을 일으킨다 "는 말을 사용해서는 안된다는 " 참고 문헌을 남용하지 마십시오 "섹션을 반드시 읽으십시오 state.

이것이 도움이되기를 바랍니다 ^ _ ^

편집 : React.createRef()심판을 만드는 방법이 추가되었습니다 . ES5 코드를 제거했습니다.


5
깔끔한 약간의 깔끔한,에서 함수를 호출 할 수 있습니다 this.refs.yourComponentNameHere. 함수를 통해 상태를 변경하는 데 유용한 것으로 나타났습니다. 예 : this.refs.textInputField.clearInput();
Dylan Pierce

7
(워드 프로세서에서)주의 : 참고 문헌은 반응성 스트리밍을 통해 할 불편할 수있는 방식으로 특정 자식 인스턴스에 메시지를 보낼 수있는 좋은 방법입니다 propsstate. 그러나 애플리케이션을 통해 데이터를 전달하기위한 추상화가되어서는 안됩니다. 기본적으로 반응성 데이터 흐름 ref을 사용하고 본질적으로 비 반응성 인 유스 케이스에 대해 저장하십시오 .
Lynn

2
난 단지 (안 최신 상태로) 생성자 내부에 정의 된 상태를 얻을
Vlado Pandžić

3
참조를 사용하는 것은 이제 ' 제어 된 구성 요소 '를 사용하도록 권장하면서 ' 제어되지 않은 구성 요소 '를 사용하는 것으로 분류됩니다
icc97

하위 컴포넌트가 Redux connected컴포넌트 인 경우이를 수행 할 수 있습니까?
jmancherje

7

그것의 2,020 당신과 많이 비슷한 솔루션을하지만, 찾고 여기에 올 것이다 후크 (그들은 중대하다!) 최신와 코드 청결과 구문의 관점에서 접근한다.

따라서 이전 답변에서 언급했듯이 이러한 종류의 문제에 대한 최선의 접근 방식은 하위 구성 요소 외부의 상태를 유지하는 것 fieldEditor입니다. 여러 가지 방법으로 그렇게 할 수 있습니다.

가장 복잡한 것은 부모와 자식 모두 액세스하고 수정할 수있는 전역 컨텍스트 (상태)입니다. 구성 요소가 트리 계층 구조에 매우 깊어서 각 수준에서 소품을 보내는 데 비용이 많이 드는 훌륭한 솔루션입니다.

이 경우에는 그만한 가치가 없다고 생각하고 더 간단한 접근 방식으로 강력한 결과를 사용하여 원하는 결과를 얻을 수 React.useState()있습니다.

클래스 컴포넌트보다 훨씬 간단한 React.useState () 후크로 접근

앞서 언급했듯이 변경 사항을 처리하고 자식 구성 요소의 데이터를 fieldEditor부모에 저장합니다 fieldForm. 이를 위해 변경 사항을 처리하고 fieldForm상태에 적용하는 함수에 대한 참조를 보내면 다음과 같이 할 수 있습니다.

function FieldForm({ fields }) {
  const [fieldsValues, setFieldsValues] = React.useState({});
  const handleChange = (event, fieldId) => {
    let newFields = { ...fieldsValues };
    newFields[fieldId] = event.target.value;

    setFieldsValues(newFields);
  };

  return (
    <div>
      {fields.map(field => (
        <FieldEditor
          key={field}
          id={field}
          handleChange={handleChange}
          value={fieldsValues[field]}
        />
      ))}
      <div>{JSON.stringify(fieldsValues)}</div>
    </div>
  );
}

참고 React.useState({})위치 0 (이 경우에는 빈 객체) 호출에 지정된 값으로되는 배열을 반환하며, 위치 (1)는 기능이 수정 값으로 참조되고.

이제 자식 구성 요소 FieldEditor를 사용하면 return 문으로 함수를 만들 필요조차 없으며 화살표 함수가있는 린 상수가 수행합니다!

const FieldEditor = ({ id, value, handleChange }) => (
  <div className="field-editor">
    <input onChange={event => handleChange(event, id)} value={value} />
  </div>
);

Aaaaand 우리는이 두 가지 점액 기능성 구성 요소만으로도 우리의 자녀 FieldEditor가치에 "접근"하고 부모에게 보여줄 목표를 가지고 있습니다 .

5 년 전에 수락 된 답변을 확인하고 Hooks가 React 코드를 어떻게 만들 었는지 확인할 수 있습니다 (많은 방법으로)!

내 답변이 Hooks에 대해 더 많이 배우고 이해하는 데 도움이되기를 바랍니다. 작동하는 예제 를 확인하려면 여기가 있습니다.


4

이제 FormEditor의 하위 인 InputField 상태에 액세스 할 수 있습니다.

기본적으로 입력 필드 (자식)의 상태가 변경 될 때마다 이벤트 객체에서 값을 가져온 다음이 값을 Parent의 상태가 설정된 Parent에 전달합니다.

버튼을 클릭하면 입력 필드의 상태가 인쇄됩니다.

여기서 중요한 점은 props를 사용하여 입력 필드의 id / value를 가져오고 재사용 가능한 자식 입력 필드를 생성하는 동안 입력 필드의 속성으로 설정된 함수를 호출한다는 것입니다.

class InputField extends React.Component{
  handleChange = (event)=> {
    const val = event.target.value;
    this.props.onChange(this.props.id , val);
  }

  render() {
    return(
      <div>
        <input type="text" onChange={this.handleChange} value={this.props.value}/>
        <br/><br/>
      </div>
    );
  }
}       


class FormEditorParent extends React.Component {
  state = {};
  handleFieldChange = (inputFieldId , inputFieldValue) => {
    this.setState({[inputFieldId]:inputFieldValue});
  }
  //on Button click simply get the state of the input field
  handleClick = ()=>{
    console.log(JSON.stringify(this.state));
  }

  render() {
    const fields = this.props.fields.map(field => (
      <InputField
        key={field}
        id={field}
        onChange={this.handleFieldChange}
        value={this.state[field]}
      />
    ));

    return (
      <div>
        <div>
          <button onClick={this.handleClick}>Click Me</button>
        </div>
        <div>
          {fields}
        </div>
      </div>
    );
  }
}

const App = () => {
  const fields = ["field1", "field2", "anotherField"];
  return <FormEditorParent fields={fields} />;
};

ReactDOM.render(<App/>, mountNode);

7
내가 이것을 공표 할 수 있는지 확실하지 않습니다. @ Markus-ipse가 아직 답변하지 않은 것은 무엇입니까?
Alexander Bird

3

이전 답변에서 말했듯이 상태를 최상위 구성 요소로 옮기고 자식에 전달 된 콜백을 통해 상태를 수정하십시오.

기능적 구성 요소 (후크)로 선언 된 자식 상태에 실제로 액세스해야하는 경우 부모 구성 요소에서 참조 를 선언 한 다음 자식에 ref 속성 으로 전달할 수 있지만 React.forwardRef 를 사용해야 합니다. 그런 다음 useImperativeHandle 훅 을 통해 상위 컴포넌트에서 호출 할 수있는 함수를 선언하십시오.

다음 예를 살펴보십시오.

const Parent = () => {
    const myRef = useRef();
    return <Child ref={myRef} />;
}

const Child = React.forwardRef((props, ref) => {
    const [myState, setMyState] = useState('This is my state!');
    useImperativeHandle(ref, () => ({getMyState: () => {return myState}}), [myState]);
})

그런 다음 다음을 호출하여 Parent 구성 요소에서 myState를 가져올 수 있어야합니다. myRef.current.getMyState();

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