code

{this.props.children}에 소품을 전달하는 방법

codestyles 2020. 9. 29. 07:49
반응형

{this.props.children}에 소품을 전달하는 방법


일반적인 방식으로 사용할 수있는 일부 구성 요소를 정의하는 적절한 방법을 찾으려고합니다.

<Parent>
  <Child value="1">
  <Child value="2">
</Parent>

물론 부모와 자녀 구성 요소 사이의 렌더링에가는 논리가있다, 당신은 상상할 수 <select><option>이 논리의 예로서.

이것은 질문의 목적을위한 더미 구현입니다.

var Parent = React.createClass({
  doSomething: function(value) {
  },
  render: function() {
    return (<div>{this.props.children}</div>);
  }
});

var Child = React.createClass({
  onClick: function() {
    this.props.doSomething(this.props.value); // doSomething is undefined
  },
  render: function() {
    return (<div onClick={this.onClick}></div>);
  }
});

문제는 {this.props.children}래퍼 구성 요소를 정의하는 데 사용할 때마다 일부 속성을 모든 자식에게 어떻게 전달합니까?


새로운 소품으로 아이들 복제하기

당신은 사용할 수 있습니다 React.Children를 복제 한 후 새로운 소품 (얕은 병합)를 사용하여 각 요소를 자식들에 대해 반복하고, React.cloneElement을 예를 들면 :

const Child = ({ doSomething, value }) => (
  <div onClick={() => doSomething(value)}>Click Me</div>
);

class Parent extends React.PureComponent {
  doSomething = value => {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    const childrenWithProps = React.Children.map(this.props.children, child =>
      React.cloneElement(child, { doSomething: this.doSomething })
    );

    return <div>{childrenWithProps}</div>
  }
};

ReactDOM.render(
  <Parent>
    <Child value="1" />
    <Child value="2" />
  </Parent>,
  document.getElementById('container')
);

바이올린 : https://jsfiddle.net/2q294y43/2/

아이들을 함수로 부르기

또한 render props를 사용 하여 소품을 자식에게 전달할 수도 있습니다 . 이 접근 방식에서 자식 ( children또는 다른 소품 이름 일 수 있음 )은 전달하려는 인수를 수락하고 자식을 반환 할 수있는 함수입니다.

const Child = ({ doSomething, value }) => (
  <div onClick={() =>  doSomething(value)}>Click Me</div>
);

class Parent extends React.PureComponent {
  doSomething = value => {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    return <div>{this.props.children(this.doSomething)}</div>
  }
};

ReactDOM.render(
  <Parent>
    {doSomething => (
      <React.Fragment>
        <Child doSomething={doSomething} value="1" />
        <Child doSomething={doSomething} value="2" />
      </React.Fragment>
    )}
  </Parent>,
  document.getElementById('container')
);

대신 <React.Fragment>또는 단순히 <>원하는 경우 배열을 반환 할 수도 있습니다.

바이올린 : https://jsfiddle.net/ferahl/y5pcua68/7/


이를 수행하는 약간 더 깨끗한 방법을 보려면 다음을 시도하십시오.

<div>
    {React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>

편집 : 여러 개별 하위 항목과 함께 사용하려면 (자식 자체가 구성 요소 여야 함) 수행 할 수 있습니다. 16.8.6에서 테스트 됨

<div>
    {React.cloneElement(props.children[0], { loggedIn: true, testingTwo: true })}
    {React.cloneElement(props.children[1], { loggedIn: true, testProp: false })}
</div>

이 시도

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

react-15.1을 사용하여 나를 위해 일했습니다.


어린이에게 소품을 전달하십시오.

다른 모든 답변보기

컨텍스트 를 통해 구성 요소 트리를 통해 공유 된 글로벌 데이터를 전달합니다.

컨텍스트는 현재 인증 된 사용자, 테마 또는 선호 언어와 같은 React 구성 요소 트리에 대해 "전역"으로 간주 될 수있는 데이터를 공유하도록 설계되었습니다. 1

면책 조항 : 이것은 업데이트 된 답변입니다. 이전 답변은 이전 컨텍스트 API를 사용했습니다.

소비자 / 제공 원칙을 기반으로합니다. 먼저 컨텍스트 생성

const { Provider, Consumer } = React.createContext(defaultValue);

그런 다음 통해 사용

<Provider value={/* some value */}>
  {children} /* potential consumers */
<Provider />

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

공급자의 후손 인 모든 소비자는 공급자의 가치 제안이 변경 될 때마다 다시 렌더링됩니다. Provider에서 하위 Consumers 로의 전파는 shouldComponentUpdate 메소드의 적용을받지 않으므로 상위 구성 요소가 업데이트에서 벗어날 때도 Consumer가 업데이트됩니다. 1

전체 예제, 반 의사 코드.

import React from 'react';

const { Provider, Consumer } = React.createContext({ color: 'white' });

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: { color: 'black' },
    };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}

class Toolbar extends React.Component {
  render() {
    return ( 
      <div>
        <p> Consumer can be arbitrary levels deep </p>
        <Consumer> 
          {value => <p> The toolbar will be in color {value.color} </p>}
        </Consumer>
      </div>
    );
  }
}

1 https://facebook.github.io/react/docs/context.html


중첩 된 자식에게 소품 전달

React 16.6으로 업데이트 하면 이제 React.createContextcontextType을 사용할 수 있습니다 .

import * as React from 'react';

// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext(); 

class Parent extends React.Component {
  doSomething = (value) => {
    // Do something here with value
  };

  render() {
    return (
       <MyContext.Provider value={{ doSomething: this.doSomething }}>
         {this.props.children}
       </MyContext.Provider>
    );
  }
}

class Child extends React.Component {
  static contextType = MyContext;

  onClick = () => {
    this.context.doSomething(this.props.value);
  };      

  render() {
    return (
      <div onClick={this.onClick}>{this.props.value}</div>
    );
  }
}


// Example of using Parent and Child

import * as React from 'react';

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <Child value={2} />
      </Parent>
    );
  }
}

React.createContextReact.cloneElement 케이스가 중첩 된 구성 요소를 처리 할 수없는 곳에서 빛납니다.

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}

를 사용할 수 있습니다 React.cloneElement. 애플리케이션에서 사용하기 전에 작동 방식을 아는 것이 좋습니다. 에서 소개되었으며 React v0.13자세한 내용은 계속 읽으십시오. 따라서이 작업과 함께 다음 작업을 수행 할 수 있습니다.

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

따라서 React 문서에서 라인을 가져 와서 모든 것이 어떻게 작동하는지, 어떻게 사용할 수 있는지 이해하십시오.

React v0.13 RC2에서 우리는 React.addons.cloneWithProps와 유사한 새로운 API를 소개 할 것입니다.

React.cloneElement(element, props, ...children);

cloneWithProps와는 달리,이 새로운 함수는 우리가 transferPropsTo의 기능이없는 것과 같은 이유로 스타일과 className을 병합하는 마법의 내장 동작을 가지고 있지 않습니다. 마법의 전체 목록이 정확히 무엇인지 아무도 모르기 때문에 코드에 대해 추론하기 어렵고 스타일에 다른 서명이있을 때 재사용하기 어렵습니다 (예 : 곧 나올 React Native에서).

React.cloneElement는 다음과 거의 동일합니다.

<element.type {...element.props} {...props}>{children}</element.type>

그러나 JSX 및 cloneWithProps와 달리 참조도 유지합니다. 이것은 당신이 그것에 대한 심판을 가진 아이를 얻는다면, 당신은 당신의 조상에게서 그것을 훔치지 않을 것임을 의미합니다. 새 요소에 동일한 참조가 첨부됩니다.

한 가지 일반적인 패턴은 자녀를 매핑하고 새 소품을 추가하는 것입니다. cloneWithProps가 ref를 잃는다 고보고 된 많은 문제가있어 코드에 대해 추론하기가 더 어렵습니다. 이제 cloneElement와 동일한 패턴을 따르면 예상대로 작동합니다. 예를 들면 :

var newChildren = React.Children.map(this.props.children, function(child) {
  return React.cloneElement(child, { foo: true })
});

참고 : React.cloneElement (child, {ref : 'newRef'})는 ref를 재정의하므로 callback-ref를 사용하지 않는 한 두 부모가 동일한 자식에 대한 참조를 가질 수 없습니다.

props는 이제 불변이기 때문에 React 0.13에 들어가는 중요한 기능이었습니다. 업그레이드 경로는 종종 요소를 복제하는 것이지만 그렇게하면 ref를 잃을 수 있습니다. 따라서 여기에 더 좋은 업그레이드 경로가 필요했습니다. Facebook에서 콜 사이트를 업그레이드하면서이 방법이 필요하다는 것을 깨달았습니다. 커뮤니티에서 동일한 피드백을 받았습니다. 따라서 우리는 최종 릴리스 전에 다른 RC를 만들기로 결정했습니다.

우리는 결국 React.addons.cloneWithProps를 폐기 할 계획입니다. 아직 그렇게하고 있지는 않지만, 자신의 용도에 대해 생각하고 대신 React.cloneElement를 사용하는 것을 고려할 수있는 좋은 기회입니다. 실제로 제거하기 전에 지원 중단 알림과 함께 릴리스를 발송할 것이므로 즉각적인 조치가 필요하지 않습니다.

여기...


속성을 이전 할 수있는 가장 좋은 방법 children은 함수와 같습니다.

예:

export const GrantParent = () => {
  return (
    <Parent>
      {props => (
        <ChildComponent {...props}>
          Bla-bla-bla
        </ChildComponent>
      )}
    </Parent>
  )
}

export const Parent = ({ children }) => {
    const somePropsHere = { //...any }
    <>
        {children(somePropsHere)}
    </>
}

나는 그것을 사용하여 작동하도록 위의 허용 대답을 해결하는 데 필요한 대신 포인터를. 이것은 map 함수의 범위 내에서 doSomething 함수가 정의 되지 않았습니다 .

var Parent = React.createClass({
doSomething: function() {
    console.log('doSomething!');
},

render: function() {
    var that = this;
    var childrenWithProps = React.Children.map(this.props.children, function(child) {
        return React.cloneElement(child, { doSomething: that.doSomething });
    });

    return <div>{childrenWithProps}</div>
}})

업데이트 :이 수정 사항은 ECMAScript 5 용이며 ES6에서는 var that = this 가 필요하지 않습니다.


한 명 이상의 아이들을 고려한 더 깨끗한 방법

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>

더 이상 필요하지 않습니다 {this.props.children}. 이제 renderin을 사용하여 자식 구성 요소를 래핑 Route하고 평소와 같이 소품을 전달할 수 있습니다.

<BrowserRouter>
  <div>
    <ul>
      <li><Link to="/">Home</Link></li>
      <li><Link to="/posts">Posts</Link></li>
      <li><Link to="/about">About</Link></li>
    </ul>

    <hr/>

    <Route path="/" exact component={Home} />
    <Route path="/posts" render={() => (
      <Posts
        value1={1}
        value2={2}
        data={this.state.data}
      />
    )} />
    <Route path="/about" component={About} />
  </div>
</BrowserRouter>

어떤 답변 도 텍스트 문자열과 같이 React 구성 요소 아닌 자식을 갖는 문제를 해결 하지 않습니다 . 해결 방법은 다음과 같습니다.

// Render method of Parent component
render(){
    let props = {
        setAlert : () => {alert("It works")}
    };
    let childrenWithProps = React.Children.map( this.props.children, function(child) {
        if (React.isValidElement(child)){
            return React.cloneElement(child, props);
        }
          return child;
      });
    return <div>{childrenWithProps}</div>

}

Parent.jsx :

import React from 'react';

const doSomething = value => {};

const Parent = props => (
  <div>
    {
      !props || !props.children 
        ? <div>Loading... (required at least one child)</div>
        : !props.children.length 
            ? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type>
            : props.children.map((child, key) => 
              React.cloneElement(child, {...props, key, doSomething}))
    }
  </div>
);

Child.jsx :

import React from 'react';

/* but better import doSomething right here,
   or use some flux store (for example redux library) */
export default ({ doSomething, value }) => (
  <div onClick={() => doSomething(value)}/>
);

및 main.jsx :

import React from 'react';
import { render } from 'react-dom';
import Parent from './Parent';
import Child from './Child';

render(
  <Parent>
    <Child/>
    <Child value='1'/>
    <Child value='2'/>
  </Parent>,
  document.getElementById('...')
);

여기에서 예를 참조하십시오 : https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview


문서에 따르면 cloneElement()

React.cloneElement(
  element,
  [props],
  [...children]
)

요소를 시작점으로 사용하여 새 React 요소를 복제하고 반환합니다. 결과 요소에는 새 소품이 얕게 병합 된 원래 요소의 소품이 있습니다. 새 자녀가 기존 자녀를 대체합니다. 원래 요소의 키와 참조는 유지됩니다.

React.cloneElement() 다음과 거의 동일합니다.

<element.type {...element.props} {...props}>{children}</element.type>

그러나 refs도 보존합니다. 이것은 당신이 그것에 대한 심판을 가진 아이를 얻는다면, 당신은 당신의 조상에게서 그것을 훔치지 않을 것임을 의미합니다. 새 요소에 동일한 참조가 첨부됩니다.

그래서 cloneElement는 아이들에게 커스텀 소품을 제공하기 위해 사용하는 것입니다. 그러나 구성 요소에 여러 자식이있을 수 있으며이를 반복해야합니다. 다른 답변이 제안하는 것은을 사용하여 매핑하는 것 React.Children.map입니다. 그러나 변경 사항 React.Children.map과 달리 React.cloneElement요소 추가 및 .$접두사로 추가 키가 있습니다. 자세한 내용은이 질문을 확인하십시오 : React.Children.map 내의 React.cloneElement로 인해 요소 키가 변경됩니다.

그것을 피하려면 대신 다음 forEach과 같은 기능으로 가야합니다.

render() {
    const newElements = [];
    React.Children.forEach(this.props.children, 
              child => newElements.push(
                 React.cloneElement(
                   child, 
                   {...this.props, ...customProps}
                )
              )
    )
    return (
        <div>{newElements}</div>
    )

}

많은 사람들이이 기능을 안티 패턴으로 간주했지만, 현재 수행중인 작업을 알고 있고 솔루션을 잘 설계하면 여전히 사용할 수 있습니다.

하위 구성 요소로 기능


소품전달 하려는 자식이 여러 개인 경우 React.Children.map을 사용하여 다음과 같이 할 수 있습니다.

render() {
    let updatedChildren = React.Children.map(this.props.children,
        (child) => {
            return React.cloneElement(child, { newProp: newProp });
        });

    return (
        <div>
            { updatedChildren }
        </div>
    );
}

구성 요소에 자식이 하나만있는 경우 매핑 할 필요가 없습니다. 바로 cloneElement 할 수 있습니다.

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}

@and_rest 답변에 더해, 이것이 내가 아이들을 복제하고 클래스를 추가하는 방법입니다.

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>

렌더 소품이이 시나리오를 처리하는 적절한 방법이라고 생각합니다.

부모 코드를 다음과 같이 리팩토링하여 부모가 자식 구성 요소에 사용되는 필수 소품을 제공하도록합니다.

const Parent = ({children}) => {
  const doSomething(value) => {}

  return children({ doSomething })
}

그런 다음 자식 구성 요소에서 부모가 제공하는 함수에 다음과 같이 액세스 할 수 있습니다.

class Child extends React {

  onClick() => { this.props.doSomething }

  render() { 
    return (<div onClick={this.onClick}></div>);
  }

}

이제 약혼자 구조는 다음과 같습니다.

<Parent>
  {(doSomething) =>
   (<Fragment>
     <Child value="1" doSomething={doSomething}>
     <Child value="2" doSomething={doSomething}>
    <Fragment />
   )}
</Parent>

이를 수행하는 가장 매끄러운 방법 :

    {React.cloneElement(this.props.children, this.props)}

단일 자식 요소가있는 사람은이를 수행해야합니다.

{React.isValidElement(this.props.children)
                  ? React.cloneElement(this.props.children, {
                      ...prop_you_want_to_pass
                    })
                  : null}

방법 1-자식 복제

const Parent = (props) => {
   const attributeToAddOrReplace= "Some Value"
   const childrenWithAdjustedProps = React.Children.map(props.children, child =>
      React.cloneElement(child, { attributeToAddOrReplace})
   );

   return <div>{childrenWithAdjustedProps }</div>
}

방법 2-구성 가능한 컨텍스트 사용

컨텍스트를 사용하면 그 사이의 구성 요소를 통해 prop으로 명시 적으로 전달하지 않고도 깊은 하위 구성 요소에 prop을 전달할 수 있습니다.

컨텍스트에는 단점이 있습니다.

  1. 데이터는 소품을 통해 정기적으로 흐르지 않습니다.
  2. 컨텍스트를 사용하면 소비자와 공급자 간의 계약이 생성됩니다. 구성 요소를 재사용하는 데 필요한 요구 사항을 이해하고 복제하는 것이 더 어려울 수 있습니다.

구성 가능한 컨텍스트 사용

export const Context = createContext<any>(null);

export const ComposableContext = ({ children, ...otherProps }:{children:ReactNode, [x:string]:any}) => {
    const context = useContext(Context)
    return(
      <Context.Provider {...context} value={{...context, ...otherProps}}>{children}</Context.Provider>
    );
}

function App() {
  return (
      <Provider1>
            <Provider2> 
                <Displayer />
            </Provider2>
      </Provider1>
  );
}

const Provider1 =({children}:{children:ReactNode}) => (
    <ComposableContext greeting="Hello">{children}</ComposableContext>
)

const Provider2 =({children}:{children:ReactNode}) => (
    <ComposableContext name="world">{children}</ComposableContext>
)

const Displayer = () => {
  const context = useContext(Context);
  return <div>{context.greeting}, {context.name}</div>;
};


이것이 당신에게 필요한 것입니까?

var Parent = React.createClass({
  doSomething: function(value) {
  }
  render: function() {
    return  <div>
              <Child doSome={this.doSomething} />
            </div>
  }
})

var Child = React.createClass({
  onClick:function() {
    this.props.doSome(value); // doSomething is undefined
  },  
  render: function() {
    return  <div onClick={this.onClick}></div>
  }
})

React.children이 나를 위해 일하지 않은 이유가 있습니다. 이것이 나를 위해 일한 것입니다.

아이에게 수업을 추가하고 싶었습니다. 소품 변경과 유사

 var newChildren = this.props.children.map((child) => {
 const className = "MenuTooltip-item " + child.props.className;
    return React.cloneElement(child, { className });
 });

 return <div>{newChildren}</div>;

여기서 트릭은 React.cloneElement 입니다. 비슷한 방식으로 소품을 전달할 수 있습니다.


Render props 는이 문제에 대한 가장 정확한 접근 방식입니다. 자식 구성 요소를 자식 소품으로 부모 구성 요소에 전달하는 대신 부모가 자식 구성 요소를 수동으로 렌더링하도록합니다. Render 는 react에 내장 된 props로 함수 매개 변수를받습니다. 이 함수에서 부모 구성 요소가 원하는대로 사용자 지정 매개 변수를 렌더링하도록 할 수 있습니다. 기본적으로 자식 소품과 동일한 작업을 수행하지만보다 사용자 정의 할 수 있습니다.

class Child extends React.Component {
  render() {
    return <div className="Child">
      Child
      <p onClick={this.props.doSomething}>Click me</p>
           {this.props.a}
    </div>;
  }
}

class Parent extends React.Component {
  doSomething(){
   alert("Parent talks"); 
  }

  render() {
    return <div className="Parent">
      Parent
      {this.props.render({
        anythingToPassChildren:1, 
        doSomething: this.doSomething})}
    </div>;
  }
}

class Application extends React.Component {
  render() {
    return <div>
      <Parent render={
          props => <Child {...props} />
        }/>
    </div>;
  }
}

Codepen의 예

참고 URL : https://stackoverflow.com/questions/32370994/how-to-pass-props-to-this-props-children

반응형