[javascript] React.js의 부모 구성 요소에 소품 전달

propsReact.js에서 이벤트를 사용하여 자녀 를 부모 에게 전달하는 간단한 방법이 있습니까?

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(event) {
    // event.component.props ?why is this not available?
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

제어 된 구성 요소를 사용하여 입력 값을 전달할 수 있지만 전체 키트 n ‘국수를 전달하는 것이 좋습니다. 때로는 하위 구성 요소에 조회 할 필요가없는 일련의 정보가 포함되어 있습니다.

아마도 구성 요소를 이벤트에 바인딩하는 방법이 있습니까?

업데이트 – 2015 년 9 월 1 일

1 년 이상 React를 사용하고 Sebastien Lorber의 답변에 의해 자극 된 후, 부모의 함수에 대한 인수로 자식 구성 요소를 전달하는 것이 실제로 React 방식 이 아니라는 것이 아니라고 결론을 내 렸습니다 . 나는 대답을 바꿨다.



답변

편집 : ES6 업데이트 된 예제의 최종 예제를 참조하십시오.

이 답변은 단순히 직접적인 부모-자식 관계의 경우를 처리합니다. 부모와 자녀가 잠재적으로 중개인이 많을 경우이 답변을 확인하십시오 .

다른 솔루션에는 요점이 없습니다

그들은 여전히 ​​잘 작동하지만 다른 답변에는 매우 중요한 것이 빠져 있습니다.

React.js에서 이벤트를 사용하여 자녀의 소품을 부모에게 전달하는 간단한 방법이 있습니까?

부모님은 이미 그 자식 소품을 가지고 있습니다! : 아이가 소품을 가지고 있다면, 그것은 부모가 아이에게 소품을 제공했기 때문입니다! 부모가 이미 그 소도구를 가지고있는 동안 왜 자녀가 소도구를 부모에게 전달하길 원합니까?

더 나은 구현

어린이 : 실제로 그보다 더 복잡 할 필요는 없습니다.

var Child = React.createClass({
  render: function () {
    return <button onClick={this.props.onClick}>{this.props.text}</button>;
  },
});

자녀가있는 부모 : 자녀 에게 전달되는 가치 사용

var Parent = React.createClass({
  getInitialState: function() {
     return {childText: "Click me! (parent prop)"};
  },
  render: function () {
    return (
      <Child onClick={this.handleChildClick} text={this.state.childText}/>
    );
  },
  handleChildClick: function(event) {
     // You can access the prop you pass to the children 
     // because you already have it! 
     // Here you have it in state but it could also be
     //  in props, coming from another parent.
     alert("The Child button text is: " + this.state.childText);
     // You can also access the target of the click here 
     // if you want to do some magic stuff
     alert("The Child HTML is: " + event.target.outerHTML);
  }
});

JS 피들

자녀 목록이있는 부모 : 부모에게 필요한 모든 것을 여전히 가지고 있으며 자녀를 더 복잡하게 만들 필요가 없습니다.

var Parent = React.createClass({
  getInitialState: function() {
     return {childrenData: [
         {childText: "Click me 1!", childNumber: 1},
         {childText: "Click me 2!", childNumber: 2}
     ]};
  },
  render: function () {
    var children = this.state.childrenData.map(function(childData,childIndex) {
        return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
    }.bind(this));
    return <div>{children}</div>;
  },

  handleChildClick: function(childData,event) {
     alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
     alert("The Child HTML is: " + event.target.outerHTML);
  }
});

JS 피들

사용 this.handleChildClick.bind(null,childIndex)후 사용 가능this.state.childrenData[childIndex]

null그렇지 않으면 React가 자동 바인딩 시스템 과 관련된 경고를 발행하기 때문에 컨텍스트 와 바인딩 합니다. null을 사용하면 함수 컨텍스트를 변경하지 않으려는 것입니다. 도 참조하십시오 .

다른 답변의 캡슐화 및 연결 정보

이것은 나에게 커플 링과 캡슐화 측면에서 나쁜 생각입니다.

var Parent = React.createClass({
  handleClick: function(childComponent) {
     // using childComponent.props
     // using childComponent.refs.button
     // or anything else using childComponent
  },
  render: function() {
    <Child onClick={this.handleClick} />
  }
});

소품 사용 : 위에서 설명한 것처럼 이미 부모에 소품이 있으므로 전체 자식 구성 요소를 전달하여 소품에 액세스하는 것은 쓸모가 없습니다.

심판 사용 : 이벤트에 이미 클릭 타겟이 있으며 대부분의 경우 충분합니다. 또한 자녀에게 직접 심판을 사용할 수 있습니다.

<Child ref="theChild" .../>

그리고 부모의 DOM 노드에 액세스하십시오.

React.findDOMNode(this.refs.theChild)

부모에서 자식의 여러 참조에 액세스하려는 고급 경우 자식은 콜백에서 직접 모든 DOM 노드를 전달할 수 있습니다.

컴포넌트에는 인터페이스 (props)가 있으며 부모는 내부 DOM 구조 또는 참조를 선언하는 DOM 노드를 포함하여 자식의 내부 작업에 대해 아무 것도 가정해서는 안됩니다. 자녀의 심판을 사용하는 부모는 두 구성 요소를 단단히 결합한다는 것을 의미합니다.

이 문제를 설명하기 위해 브라우저 내에서 슬라이더, 스크롤 막대, 비디오 플레이어와 같은 것을 렌더링하는 데 사용되는 Shadow DOM 에 대해이 인용문을 가져옵니다 .

이들은 웹 개발자가 도달 할 수있는 것과 구현 세부 사항으로 간주되는 것 사이에 경계를 만들었으므로 액세스 할 수 없습니다. 그러나 브라우저는이 경계를 자유롭게 통과 할 수 있습니다. 이러한 경계가 정해지면, 그들은 div와 범위에서 같은 오래된 웹 기술을 사용하여 모든 HTML 요소를 원하는대로 구축 할 수있었습니다.

문제는 자식 구현 세부 정보가 부모에게 유출되도록하면 부모에게 영향을 미치지 않고 자식을 리팩터링하는 것이 매우 어렵다는 것입니다. 이것은 라이브러리 작성자 (또는 Shadow DOM을 사용하는 브라우저 편집기)로서 클라이언트가 너무 많은 액세스를 허용하여 역 호환성을 해치지 않고 코드를 업그레이드하기가 매우 어렵 기 때문에 매우 위험하다는 것을 의미합니다.

Chrome에서 클라이언트가 해당 스크롤 막대의 내부 dom 노드에 액세스 할 수 있도록 스크롤 막대를 구현 한 경우 클라이언트가 해당 스크롤 막대를 간단하게 중단 할 수 있으며 Chrome에서 리팩터링 후 자동 업데이트를 수행하면 앱이 더 쉽게 중단 될 수 있습니다. scrollbar … 대신 CSS를 사용하여 스크롤 막대의 일부를 사용자 정의하는 것과 같은 안전한 것들에만 액세스 할 수 있습니다.

다른 것을 사용하는 것에 대하여

콜백에서 전체 구성 요소를 전달하는 것은 위험하며 초보자 개발자가 부모 내부에서 또는를 호출 childComponent.setState(...)하거나 childComponent.forceUpdate()새 변수를 부모 내부에 할당하는 것과 같은 매우 이상한 일을 수행 하여 전체 앱을 추론하기가 훨씬 더 어려워 질 수 있습니다.


편집 : ES6 예제

많은 사람들이 이제 ES6을 사용함에 따라 ES6 구문과 동일한 예가 있습니다.

아이는 매우 간단 할 수 있습니다.

const Child = ({
  onClick,
  text
}) => (
  <button onClick={onClick}>
    {text}
  </button>
)

부모는 클래스가 될 수 있습니다 (그리고 결국 상태 자체를 관리 할 수는 있지만 여기에 소품으로 전달합니다.

class Parent1 extends React.Component {
  handleChildClick(childData,event) {
     alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
     alert("The Child HTML is: " + event.target.outerHTML);
  }
  render() {
    return (
      <div>
        {this.props.childrenData.map(child => (
          <Child
            key={child.childNumber}
            text={child.childText}
            onClick={e => this.handleChildClick(child,e)}
          />
        ))}
      </div>
    );
  }
}

그러나 상태를 관리 할 필요가없는 경우 단순화 할 수도 있습니다.

const Parent2 = ({childrenData}) => (
  <div>
     {childrenData.map(child => (
       <Child
         key={child.childNumber}
         text={child.childText}
         onClick={e => {
            alert("The Child button data is: " + child.childText + " - " + child.childNumber);
                    alert("The Child HTML is: " + e.target.outerHTML);
         }}
       />
     ))}
  </div>
)

JS 피들


PERF 경고 당신이 사용하는 경우 : (ES5 / ES6에 적용) PureComponent또는 shouldComponentUpdate사용하기 때문에 위의 구현은 기본적으로 최적화되지 않습니다 onClick={e => doSomething()}, 또는 렌더링 단계에서 직접 바인딩은 부모가 렌더링 새로운 기능 매번를 만들 수 있기 때문에. 앱에서 성능 병목 현상이 발생하면 데이터를 하위 항목으로 전달하고 “안정된”콜백 (부모 클래스에서 설정 this하고 클래스 생성자에서 바인딩) 내에 데이터를 다시 삽입하여 PureComponent최적화를 시작할 수 있습니다. shouldComponentUpdate소품 비교 검사에서 자신의 구현을 수행 하고 콜백을 무시할 수 있습니다 .

또한 재구성 라이브러리를 사용 하여 고급 구성 요소를 제공하여 미세 조정 된 최적화를 달성 할 수 있습니다.

// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}

// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)

// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)

이 경우 다음을 사용하여 하위 구성 요소를 최적화 할 수 있습니다.

const OptimizedChild = onlyUpdateForKeys(['text'])(Child)


답변

업데이트 (9/1/15) : OP는이 질문을 약간 움직이는 대상으로 만들었습니다. 다시 업데이트되었습니다. 따라서 회신을 업데이트 할 책임이 있습니다.

먼저 제공된 예제에 대한 답변 :

예, 가능합니다.

Child onClick를 다음과 this.props.onClick.bind(null, this)같이 업데이트하면이 문제를 해결할 수 있습니다 .

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
  }
});

부모의 이벤트 핸들러는 다음과 같이 컴포넌트와 이벤트에 액세스 할 수 있습니다.

  onClick: function (component, event) {
    // console.log(component, event);
  },

JSBin 스냅 샷

그러나 질문 자체는 오도의 소지가 있습니다

부모님은 이미 Child ‘s를 알고 props있습니다.

소품이 실제로 제공되지 않기 때문에 제공된 예제에서는 명확하지 않습니다. 이 샘플 코드는 묻는 질문을 더 잘 지원할 수 있습니다.

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick}> {this.props.text} </a>;
  }
});

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (event) {
    // event.component.props ?why is this not available? 
  },
  render: function() {
    return <Child onClick={this.onClick} text={this.state.text} />;
  }
});

이 예에서는 Child의 소품이 무엇인지 이미 알고 있습니다.

JSBin 스냅 샷

자녀의 소품을 사용하는 것이 중요하다면…

자녀의 소품을 사용하는 것에 관한 것이면 자녀와의 만남을 피할 수 있습니다.

JSX에는 Child와 같은 구성 요소에서 자주 사용 하는 스프레드 속성 API가 있습니다. 모든 것을 가져 와서 props구성 요소에 적용합니다. 아이는 다음과 같이 보일 것입니다 :

var Child = React.createClass({
  render: function () {
    return <a {...this.props}> {this.props.text} </a>;
  }
});

부모에서 직접 값을 사용할 수 있도록 허용 :

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
  }
});

JSBin 스냅 샷

추가 하위 구성 요소를 연결할 때 추가 구성이 필요하지 않습니다.

var Parent = React.createClass({
  getInitialState: function () {
    return {
      text: "Click here",
      text2: "No, Click here",
    };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <div>
      <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
      <Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
    </div>;
  }
});

JSBin 스냅 샷

그러나 이것이 실제 사용 사례가 아닌 것 같습니다. 그럼 더 파헤쳐 보자…

강력한 실제 사례

제공된 예제의 일반적인 특성은 말하기 어렵습니다. 위의 질문에 대한 실제적인 사용을 시연하고 매우 반응적인 방식으로 구현 된 구성 요소를 만들었습니다 .

DTServiceCalculator 작업 예제
DTServiceCalculator 저장소

이 구성 요소는 간단한 서비스 계산기입니다. 서비스 목록 (이름과 가격 포함)을 제공하면 선택한 총 가격이 계산됩니다.

아이들은 행복하게 무지하다

ServiceItem이 예에서 하위 구성 요소입니다. 외부 세계에 대한 의견이 많지 않습니다. 그것은 몇 가지 소품을 필요로 클릭 할 때 호출되는 함수 중 하나를.

<div onClick={this.props.handleClick.bind(this.props.index)} />

handleClick제공된 index[ source ]로 제공된 콜백 을 호출하는 것 외에는 아무것도하지 않습니다 .

부모는 아이들이다

DTServicesCalculator이 예는 상위 구성 요소입니다. 또한 아이입니다. 한번 보자.

DTServiceCalculator하위 컴포넌트 목록을 작성하고 ServiceItemprops [ source ]를 제공합니다 . 그것은 부모 구성 요소 ServiceItem이지만 목록을 전달하는 구성 요소의 자식 구성 요소입니다. 데이터를 소유하지 않습니다. 따라서 구성 요소 처리를 부모 구성 요소 소스에 다시 위임 합니다.

<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />

handleServiceItem인덱스를 캡처하여 자식에서 전달한 다음 부모에게 제공합니다. [ source ]

handleServiceClick (index) {
  this.props.onSelect(index);
}

소유자는 모든 것을 알고 있습니다

“소유”라는 개념은 React에서 중요한 것입니다. 자세한 내용은 여기를 참조 하십시오 .

내가 보여준 예에서, 상태를 소유 한 컴포넌트에 도달 할 때까지 컴포넌트 트리에서 이벤트 처리를 위임합니다.

마지막으로 도착하면 [ source ] 와 같이 상태 선택 / 선택 취소를 처리합니다 .

handleSelect (index) {
  let services = […this.state.services];
  services[index].chosen = (services[index].chosen) ? false : true;
  this.setState({ services: services });
}

결론

가장 바깥 쪽의 구성 요소를 최대한 불투명하게 유지하십시오. 부모 구성 요소가이를 구현하기 위해 선택할 수있는 방법에 대한 환경 설정이 거의 없는지 확인하십시오.

조작중인 데이터를 소유 한 사람을 알고 있어야합니다. 대부분의 경우 트리를 처리하는 이벤트를 해당 상태 를 소유 한 구성 요소에 위임해야합니다 .

따로 : Flux 패턴 은 앱에서 이러한 유형의 필수 연결을 줄이는 좋은 방법입니다.


답변

간단한 답변이있는 것 같습니다. 이걸 고려하세요:

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick.bind(null, this)}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(component, event) {
    component.props // #=> {Object...}
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

열쇠는 호출되어 bind(null, this)this.props.onClick부모에서 전달 된 이벤트. 이제 온 클릭 함수는 인수를 받아 component, ANDevent . 나는 그것이 모든 세계에서 최고라고 생각합니다.

업데이트 : 2015 년 9 월 1 일

이것은 나쁜 생각이었습니다. 자식 구현 세부 정보를 부모에게 유출시키는 것은 결코 좋은 길이 아닙니다. Sebastien Lorber의 답변을 참조하십시오.


답변

문제는 자식에서 부모 구성 요소로 인수를 전달하는 방법입니다. 이 예제는 사용하기 쉽고 테스트되었습니다.

//Child component
class Child extends React.Component {
    render() {
        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
        )
    }
}

//Parent component
class Parent extends React.Component {
    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
    }

    handleToUpdate(someArg){
        alert('We pass argument from Child to Parent: \n' + someArg);
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;
        return (<div>
          <Child handleToUpdate = {handleToUpdate.bind(this)} />
        </div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <Parent />,
        document.querySelector("#demo")
    );
}

JSFIDDLE을보십시오


답변

기본적으로 소품을 사용하여 Child and Parent와 정보를주고받습니다.

훌륭한 답변에 모두 추가하여 React에서 자식에서 부모 구성 요소로 값을 전달하는 방법을 설명하는 간단한 예를 들어 보겠습니다.

App.js

class App extends React.Component {
      constructor(){
            super();
            this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
            this.state={name:'igi'}
      }
      handleFilterUpdate(filterValue) {
            this.setState({
                  name: filterValue
            });
      }
   render() {
      return (
        <div>
            <Header change={this.handleFilterUpdate} name={this.state.name} />
            <p>{this.state.name}</p>
        </div>
      );
   }
}

Header.js

class Header extends React.Component {
      constructor(){
            super();
            this.state={
                  names: 'jessy'
            }
      }
      Change(event) {

      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

   render() {
      return (
       <button onClick={this.Change.bind(this)}>click</button>

      );
   }
}

Main.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

ReactDOM.render(<App />, document.getElementById('app'));

이제 클라이언트에서 서버로 값을 전달할 수 있습니다.

Header.js의 변경 기능을 살펴보십시오.

Change(event) {
      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

클라이언트에서 서버로 소품으로 값을 푸시하는 방법입니다.


답변

다음은 부모 생성자에서 함수 바인딩을 사용하는 간단한 3 단계 ES6 구현입니다. 이것은 공식 반응 튜토리얼이 권장하는 첫 번째 방법입니다 (여기서는 다루지 않는 공개 클래스 필드 구문이 있습니다). https://reactjs.org/docs/handling-events.html 에서이 모든 정보를 찾을 수 있습니다.

아이들이 그들을 호출 할 수 있도록 부모 함수 바인딩 (그리고 부모에게 데이터를 전달하십시오! : D)

  1. 부모 생성자에서 부모에서 만든 함수를 바인딩해야합니다.
  2. 바인딩 된 함수를 소품으로 자식에게 전달하십시오 (함수에 참조를 전달하기 때문에 람다 없음)
  3. 자식 이벤트에서 바운드 함수를 호출합니다 (Lambda! 이벤트가 시작될 때 함수를 호출합니다. 그렇지 않으면 함수가로드시 자동으로 실행되고 이벤트에서 트리거되지 않습니다).

부모 기능

handleFilterApply(filterVals){} 

부모 생성자

this.handleFilterApply = this.handleFilterApply.bind(this);

아동에게 전달 된 소품

onApplyClick = {this.handleFilterApply}

어린이 이벤트 전화

onClick = {() => {props.onApplyClick(filterVals)}


답변

이것은 onClick 이벤트를 사용하지 않는 예입니다. 소품으로 콜백 함수를 단순히 자식에게 전달합니다. 이 콜백을 사용하면 하위 통화도 데이터를 다시 보냅니다. 나는 문서 의 예제에서 영감을 받았다 .

작은 예 (이것은 tsx 파일에 있으므로 소품과 상태를 완전히 선언해야하며 구성 요소에서 일부 논리를 삭제 했으므로 코드가 적습니다).

* 업데이트 : 중요한 것은 이것을 콜백에 바인딩하는 것입니다. 그렇지 않으면 콜백은 부모가 아닌 자식의 범위를 갖습니다. 유일한 문제 : 그것은 “오래된”부모입니다 …

SymptomChoser는 부모입니다.

interface SymptomChooserState {
  // true when a symptom was pressed can now add more detail
  isInDetailMode: boolean
  // since when user has this symptoms
  sinceDate: Date,
}

class SymptomChooser extends Component<{}, SymptomChooserState> {

  state = {
    isInDetailMode: false,
    sinceDate: new Date()
  }

  helloParent(symptom: Symptom) {
    console.log("This is parent of: ", symptom.props.name);
    // TODO enable detail mode
  }

  render() {
    return (
      <View>
        <Symptom name='Fieber' callback={this.helloParent.bind(this)} />
      </View>
    );
  }
}

증상은 자식입니다 (자식 소품에서 콜백 함수를 선언했습니다. selectedSymptom에서 콜백이 호출되었습니다).

interface SymptomProps {
  // name of the symptom
  name: string,
  // callback to notify SymptomChooser about selected Symptom.
  callback: (symptom: Symptom) => void
}

class Symptom extends Component<SymptomProps, SymptomState>{

  state = {
    isSelected: false,
    severity: 0
  }

  selectedSymptom() {
    this.setState({ isSelected: true });
    this.props.callback(this);
  }

  render() {
    return (
      // symptom is not selected
      <Button
        style={[AppStyle.button]}
        onPress={this.selectedSymptom.bind(this)}>
        <Text style={[AppStyle.textButton]}>{this.props.name}</Text>
      </Button>
    );
  }
}