How to pass props to {this.props.children}

后端 未结 26 3418
猫巷女王i
猫巷女王i 2020-11-21 23:42

I\'m trying to find the proper way to define some components which could be used in a generic way:


  
  

        
相关标签:
26条回答
  • 2020-11-22 00:25

    Pass props to direct children.

    See all other answers

    Pass shared, global data through the component tree via context

    Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. 1

    Disclaimer: This is an updated answer, the previous one used the old context API

    It is based on Consumer / Provide principle. First, create your context

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

    Then use via

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

    and

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

    All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update. 1

    Full example, semi-pseudo code.

    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

    0 讨论(0)
  • 2020-11-22 00:25

    Render props is most accurate approach to this problem. Instead of passing the child component to parent component as children props, let parent render child component manually. Render is built-in props in react, which takes function parameter. In this function you can let parent component render whatever you want with custom parameters. Basically it does the same thing as child props but it is more customizable.

    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>;
      }
    }
    

    Example at codepen

    0 讨论(0)
  • 2020-11-22 00:27

    Further to @and_rest answer, this is how I clone the children and add a class.

    <div className="parent">
        {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
    </div>
    
    0 讨论(0)
  • 2020-11-22 00:29

    Try this

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

    It worked for me using react-15.1.

    0 讨论(0)
  • 2020-11-22 00:31

    Method 1 - clone children

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

    Method 2 - use composable context

    Context allows you to pass a prop to a deep child component without explicitly passing it as a prop through the components in between.

    Context comes with drawbacks:

    1. Data doesn't flow in the regular way - via props.
    2. Using context creates a contract between the consumer and the provider. It might be more difficult to understand and replicate the requirements needed to reuse a component.

    Using a composable context

    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>;
    };
    
    
    0 讨论(0)
  • 2020-11-22 00:32

    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)}/>
    );
    

    and 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('...')
    );
    

    see example here: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview

    0 讨论(0)
提交回复
热议问题