Hoe rekwisieten doorgeven aan {this.props.children}

Ik probeer de juiste manier te vinden om enkele componenten te definiëren die op een generieke manier kunnen worden gebruikt:

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

Er is natuurlijk een logica gaande voor het renderen tussen bovenliggende en onderliggende componenten, u kunt zich <select>en <option>voorstellen als een voorbeeld hiervan logica.

Dit is een dummy-implementatie voor het doel van de vraag:

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

De vraag is wanneer je {this.props.children}gebruikt om een wrapper-component te definiëren, hoe geef je een eigenschap door aan al zijn onderliggende?


Antwoord 1, autoriteit 100%

Kinderen klonen met nieuwe rekwisieten

U kunt React.Childrengebruiken om herhaal de kinderen en kloon vervolgens elk element met nieuwe rekwisieten (ondiep samengevoegd) met behulp van React.cloneElement. Bijvoorbeeld:

const Child = ({ doSomething, value }) => (
  <button onClick={() => doSomething(value)}>Click Me</button>
);
function Parent({ children }) {
  function doSomething(value) {
    console.log("doSomething called by child with value:", value);
  }
  const childrenWithProps = React.Children.map(children, child => {
    // Checking isValidElement is the safe way and avoids a typescript
    // error too.
    if (React.isValidElement(child)) {
      return React.cloneElement(child, { doSomething });
    }
    return child;
  });
  return <div>{childrenWithProps}</div>
}
function App() {
  return (
    <Parent>
      <Child value={1} />
      <Child value={2} />
    </Parent>
  );
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="container"></div>

Antwoord 2, autoriteit 39%

Voor een iets schonere manier om het te doen, probeer:

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

Bewerken:
Om met meerdere individuele kinderen te gebruiken (het kind moet zelf een onderdeel zijn) kun je dat doen. Getest in 16.8.6

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

Antwoord 3, autoriteit 8%

Probeer dit

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

Het werkte voor mij met react-15.1.


Antwoord 4, autoriteit 7%

Geef rekwisieten door om kinderen te dirigeren.

Bekijk alle andere antwoorden

Gedeelde, globale gegevens doorgeven via de componentenstructuur via context

Context is ontworpen om gegevens te delen die als ‘algemeen’ kunnen worden beschouwd voor een boomstructuur van React-componenten, zoals de huidige geverifieerde gebruiker, het thema of de voorkeurstaal. 1

Disclaimer: dit is een bijgewerkt antwoord, de vorige gebruikte de oude context-API

Het is gebaseerd op het principe Consumer / Provide. Maak eerst uw context

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

Gebruik dan via

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

en

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

Alle consumenten die afstammen van een aanbieder, worden opnieuw weergegeven wanneer de waarde van de aanbieder verandert. De verspreiding van Provider naar zijn afstammeling Consumenten is niet onderworpen aan de shouldComponentUpdate-methode, dus de Consument wordt bijgewerkt, zelfs wanneer een vooroudercomponent uit de update komt.1

Volledig voorbeeld, semi-pseudocode.

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

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


Antwoord 5, autoriteit 5%

Rekwisieten doorgeven aan geneste kinderen

Met de update naar React 16.6kun je nu React.createContexten contextTypegebruiken.

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.createContextschittert waar React.cloneElementcase geen geneste componenten aankan

class SomeComponent extends React.Component {
  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}

Antwoord 6, autoriteit 3%

De beste manier om eigendom over te dragen is childrenals een functiepatroon
https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9

Codefragment: https://stackblitz.com/edit/react-fcmubc

Voorbeeld:

const Parent = ({ children }) => {
    const somePropsHere = {
      style: {
        color: "red"
      }
      // any other props here...
    }
    return children(somePropsHere)
}
const ChildComponent = props => <h1 {...props}>Hello world!</h1>
const App = () => {
  return (
    <Parent>
      {props => (
        <ChildComponent {...props}>
          Bla-bla-bla
        </ChildComponent>
      )}
    </Parent>
  )
}

Antwoord 7, Autoriteit 2%

U kunt React.cloneElement, het is beter om te weten hoe het werkt voordat u deze in uw toepassing gebruikt. Het wordt geïntroduceerd in React v0.13, lees verder voor meer informatie, dus iets samen met dit werk voor u:

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

Breng dus de lijnen van de beoogde documentatie voor u om te begrijpen hoe het allemaal werkt en hoe u er gebruik van kunt maken:

In react v0.13 RC2 zullen we een nieuwe API introduceren, vergelijkbaar met
React.Addons.ClonewithitProps, met deze handtekening:

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

In tegenstelling tot ClonewithProps heeft deze nieuwe functie geen magie
Ingebouwd gedrag voor het samenvoegen van stijl en classname om dezelfde reden
We hebben die functie niet van TransferPropSto. Niemand is er zeker van
precies de volledige lijst met magische dingen zijn, wat het maakt
moeilijk om te redeneren over de code en moeilijk te hergebruiken wanneer stijl
heeft een andere handtekening (bijvoorbeeld in de komende reageer native).

React.cloneElement is bijna gelijk aan:

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

In tegenstelling tot JSX en cloneWithProps, behoudt het echter ook refs. Dit
betekent dat als je een kind krijgt met een scheidsrechter erop, je dat niet per ongeluk doet
steel het van je voorouder. U krijgt dezelfde ref gekoppeld aan
jouw nieuwe element.

Een veelvoorkomend patroon is om je kinderen in kaart te brengen en een nieuwe prop toe te voegen.
Er waren veel problemen gemeld over cloneWithProps die de ref verloor,
waardoor het moeilijker wordt om over uw code te redeneren. Nu hetzelfde volgen
patroon met cloneElement werkt zoals verwacht. Bijvoorbeeld:

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

Opmerking: React.cloneElement(child, { ref: ‘newRef’ }) overschrijft de
ref dus het is nog steeds niet mogelijk voor twee ouders om een ref te hebben naar de
hetzelfde kind, tenzij je callback-refs gebruikt.

Dit was een cruciale functie om in React 0.13 te komen, aangezien rekwisieten nu beschikbaar zijn
onveranderlijk. Het upgradepad is vaak om het element te klonen, maar door
hierdoor zou je de ref kunnen verliezen. Daarom hadden we een mooiere upgrade nodig
pad hier. Toen we callsites op Facebook aan het upgraden waren, realiseerden we ons dat:
we hadden deze methode nodig. We kregen dezelfde feedback van de community.
Daarom hebben we besloten om nog een RC te maken voor de definitieve release om
zorg ervoor dat we dit binnen krijgen.

We zijn van plan om uiteindelijk React.addons.cloneWithProps af te schaffen. Waren niet
doen, maar dit is een goede gelegenheid om over na te denken
uw eigen gebruik en overweeg in plaats daarvan bij het gebruik van react.cloneelement. We zullen zijn
Zeker om een ​​release met afschrijvingskennisgevingen te verzenden voordat we eigenlijk zijn
Verwijder het, dus er is geen onmiddellijke actie nodig.

meer hier


Antwoord 8

Ik moest hierboven geaccepteerd antwoord oplossen om het te laten werken met dat in plaats van deze aanwijzer. Dit binnen de reikwijdte van de kaartfunctie had niet dossomething functie gedefinieerd.

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

Update: deze fix is ​​voor Ecmascript 5, in ES6 is er geen behoefte aan var dat = dit


Antwoord 9

Schonere manier gezien een of meer kinderen

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

Antwoord 10

Geen van de antwoorden aanpakken de kwestie van het hebben van kinderen die niet reageercomponenten, zoals tekststrings zijn. Een oplossing zou zoiets kunnen zijn:

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

Antwoord 11

Als je meerdere kinderen hebt aan wie je rekwisieten wilt doorgeven, kun je dat op deze manier doen , met behulp van de React.Children.map:

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

Als uw component slechts één kind heeft, hoeft u deze niet in kaart te brengen, u kunt het element gewoon klonen:

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

Antwoord 12

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

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

Zie hier:


Antwoord 13

U hebt niet langer nodig {this.props.children}. Nu kunt u uw kindcomponent inpakken met renderin Routeen passeer uw rekwisieten zoals gewoonlijk:

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

Antwoord 14

Ik raakte geïnspireerd door alle bovenstaande antwoorden en dit is wat ik heb gedaan. Ik geef wat rekwisieten door, zoals wat gegevens en wat componenten.

import React from "react";
const Parent = ({ children }) => {
  const { setCheckoutData } = actions.shop;
  const { Input, FieldError } = libraries.theme.components.forms;
  const onSubmit = (data) => {
    setCheckoutData(data);
  };
  const childrenWithProps = React.Children.map(
    children,
    (child) =>
      React.cloneElement(child, {
        Input: Input,
        FieldError: FieldError,
        onSubmit: onSubmit,
      })
  );
  return <>{childrenWithProps}</>;
};

Antwoord 15

Vervolg op het antwoord van @and_rest, dit is hoe ik de kinderen kloon en een klas toevoeg.

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

Antwoord 16

Misschien kunt u deze functie ook nuttig vinden, hoewel veel mensen dit als een anti-patroon beschouwen, kan het nog steeds worden gebruikt als u weet wat u doet en uw oplossing goed ontwerpt.

Functie als onderliggende componenten


Antwoord 17

Ik denk dat een renderprop de juiste manier is om met dit scenario om te gaan

Je laat de ouder de benodigde rekwisieten leveren die in de onderliggende component worden gebruikt, door de bovenliggende code te herstructureren om er ongeveer zo uit te zien:

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

Dan kun je in de onderliggende component toegang krijgen tot de functie die door de ouder op deze manier wordt geleverd:

class Child extends React {
  onClick() => { this.props.doSomething }
  render() { 
    return (<div onClick={this.onClick}></div>);
  }
}

De fianl-structuur ziet er nu als volgt uit:

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

Antwoord 18

Volgens de documentatie van cloneElement()

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

Kloon en retourneer een nieuw React-element met element als startpunt
punt. Het resulterende element heeft de rekwisieten van het originele element
met de nieuwe rekwisieten ondiep samengevoegd. Nieuwe kinderen zullen vervangen
bestaande kinderen. key en ref van het originele element zal zijn
bewaard gebleven.

React.cloneElement()is bijna gelijk aan:

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

Het behoudt echter ook refs. Dit betekent dat als je een kind krijgt
met een ref erop, steel je hem niet per ongeluk van je voorouder.
Je krijgt dezelfde ref gekoppeld aan je nieuwe element.

Dus cloneElement is wat je zou gebruiken om aangepaste rekwisieten aan de kinderen te geven. Er kunnen echter meerdere kinderen in de component zijn en u moet er een lus over maken. Wat andere antwoorden suggereren, is dat je ze in kaart brengt met behulp van React.Children.map. Echter, React.Children.mapverandert in tegenstelling tot React.cloneElementde sleutels van het Element toevoegen en extra .$als prefix. Controleer deze vraag voor meer details: React.cloneElement in React.Children.map zorgt ervoor dat elementsleutels veranderen

Als je dit wilt vermijden, moet je in plaats daarvan voor de functie forEachgaan zoals

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

Antwoord 19

Voor iedereen die een enkel onderliggend element heeft, zou dit het moeten doen.

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

Antwoord 20

Methode 1 – kinderen klonen

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

Methode 2 – Gebruik de samengestelde context

Context Hiermee kunt u een prop voor een diepe kindercomponent passeren zonder het expliciet uit te voeren als een prop via de componenten ertussen.

Context wordt geleverd met nadelen:

  1. Gegevens stromen niet op de gewone manier – via rekwisieten.
  2. Het gebruik van context creëert een contract tussen de consument en de provider. Het kan moeilijker zijn om de vereisten die nodig zijn om een ​​component te hergebruiken te begrijpen en te repliceren.

met behulp van een componeerbare 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>;
};

Antwoord 21

De slimste manier om dit te doen:

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

Antwoord 22

Als je functionele componenten gebruikt, krijg je vaak de fout TypeError: Cannot add property myNewProp, object is not extensiblewanneer je probeert nieuwe eigenschappen in te stellen op props.children. Er is een oplossing om dit te omzeilen door de rekwisieten te klonen en vervolgens het kind zelf te klonen met de nieuwe rekwisieten.

const MyParentComponent = (props) => {
  return (
    <div className='whatever'>
      {props.children.map((child) => {
        const newProps = { ...child.props }
        // set new props here on newProps
        newProps.myNewProp = 'something'
        const preparedChild = { ...child, props: newProps }
        return preparedChild
      })}
    </div>
  )
}

Antwoord 23

Ik kwam naar dit bericht terwijl ik onderzoek deed naar een vergelijkbare behoefte, maar ik vond dat de kloonoplossing zo populair is, dat deze te ruw is en mijn aandacht wegleidt van de functionaliteit.

Ik heb een artikel gevonden in react-documenten Hogere-ordecomponenten

Hier is mijn voorbeeld:

import React from 'react';
const withForm = (ViewComponent) => {
    return (props) => {
        const myParam = "Custom param";
        return (
            <>
                <div style={{border:"2px solid black", margin:"10px"}}>
                    <div>this is poc form</div>
                    <div>
                        <ViewComponent myParam={myParam} {...props}></ViewComponent>
                    </div>
                </div>
            </>
        )
    }
}
export default withForm;
const pocQuickView = (props) => {
    return (
        <div style={{border:"1px solid grey"}}>
            <div>this is poc quick view and it is meant to show when mouse hovers over a link</div>
        </div>
    )
}
export default withForm(pocQuickView);

Voor mij vond ik een flexibele oplossing bij de implementatie van het patroon van componenten van hogere orde.

Natuurlijk hangt het af van de functionaliteit, maar het is goed als iemand anders op zoek is naar een vergelijkbare vereiste, het is veel beter dan afhankelijk zijn van ruw niveau reageercode zoals klonen.

ander patroon dat ik actief gebruik is, is het containerpatroon. Lees erover, er zijn veel artikelen die er zijn.


Antwoord 24

Is dit wat je nodig hebt?

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

Antwoord 25

Een of andere reden reageer. Children werkte niet voor mij. Dit is wat voor mij werkte.

Ik wilde gewoon een klasse toevoegen aan het kind. vergelijkbaar met het veranderen van een prop

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

De truc hier is de react.cloneElement . U kunt elke prop op een vergelijkbare manier passeren


Antwoord 26

render rekwisieten is de meest nauwkeurige benadering van dit probleem. In plaats van de kindercomponent te passeren naar moedercomponent als kinderen rekwisieten, maakt de ouders een kindcomponent handmatig terug. Render is ingebouwde rekwisieten in reageer, die functieparameter neemt. In deze functie kunt u moedertooncomponent laten renderen wat u wilt met aangepaste parameters. Eigenlijk doet het hetzelfde als kinderritten, maar het is meer aanpasbaar.

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

voorbeeld op COCEEPEN


Antwoord 27

Dit antwoord is W.R.T. Reageer v17.x …

Gebruik de childrenals een functie en passeer rekwisieten als een render propsPATROON, zoals hieronder: –

<ParentComponent {...anyAdditionalProps}>
   {
     (actualPropsToPass) => <ChildComponent>{children(actualPropsToPass)}</ChildComponent>
   }
 </ParentComponent>

Zorg ervoor dat de daadwerkelijke, te projecteren inhoud moet worden toegevoegd als een functie op het renderen van rekwisieten, om tegemoet te komen aan het argument dat wordt gepasseerd als een propbinnen de functie voor kinderen.


Antwoord 28

Er zijn veel manieren om dit te doen.

U kunt kinderen passeren als rekwisieten in de ouder.

Voorbeeld 1 :

function Parent({ChildElement}){
   return <ChildElement propName={propValue} />
}
return <Parent ChildElement={ChildComponent}/>

Pas kinderen als functie

Voorbeeld 2 :

function Parent({children}){
   return children({className: "my_div"})
}
OR
function Parent({children}){
   let Child = children
   return <Child className='my_div' />
}
function Child(props){
  return <div {...props}></div>
}
export <Parent>{props => <Child {...props} />}</Parent>

Other episodes