Pregunta Cómo pasar accesorios a {this.props.children}


Estoy tratando de encontrar la manera correcta de definir algunos componentes que podrían usarse de forma genérica:

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

Existe una lógica para renderizar entre los componentes padre e hijos por supuesto, puedes imaginar <select> y <option> como un ejemplo de esta lógica.

Esta es una implementación ficticia para el propósito de la pregunta:

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

La pregunta es siempre que uses {this.props.children} para definir un componente contenedor, ¿cómo transfieres alguna propiedad a todos sus hijos?


560
2017-09-03 08:47


origen


Respuestas:


Puedes usar React.Children para iterar sobre los niños, y luego clonar cada elemento con nuevos apoyos (fusión superficial) usando React.cloneElement p.ej:

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 { children } = this.props;

    const childrenWithProps = React.Children.map(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')
);

Violín: https://jsfiddle.net/2q294y43/2/


625
2017-09-03 09:17



Para una forma un poco más limpia de hacerlo, intente:

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

Nota: esto solo funcionará si hay un único hijo, y es un elemento Reaccionar válido.


288
2018-01-30 13:20



Prueba esto

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

Me funcionó usando react-15.1.


58
2018-06-06 14:42



Pase accesorios para dirigir a los niños.

Ver todas las otras respuestas

Pasar datos globales compartidos a través del árbol de componentes a través de contexto

El contexto está diseñado para compartir datos que se pueden considerar "globales" para un árbol de componentes de React, como el usuario autenticado actual, el tema o el idioma preferido. 1

Descargo de responsabilidad: Esta es una respuesta actualizada, la anterior usó la antigua API de contexto

Se basa en el principio de consumidor / proveedor. Primero, crea tu contexto

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

Entonces usa vía

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

y

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

Todos los consumidores que sean descendientes de un proveedor volverán a presentar cada vez que cambie el valor del proveedor. La propagación del Proveedor a sus Consumidores descendientes no está sujeta al método shouldComponentUpdate, por lo que el Cliente se actualiza incluso cuando un componente antecesor abandona la actualización.   1

Ejemplo completo, semi-pseudo código.

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


39
2017-09-08 22:45



Puedes usar React.cloneElement, es mejor saber cómo funciona antes de comenzar a usarlo en su aplicación. Se introduce en React v0.13, sigue leyendo para obtener más información, así que algo junto con este trabajo es para ti:

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

Así que traiga las líneas de la documentación de React para que comprenda cómo está funcionando y cómo puede hacer uso de ellas:

En React v0.13 RC2 presentaremos una nueva API, similar a   React.addons.cloneWithProps, con esta firma:

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

A diferencia de cloneWithProps, esta nueva función no tiene ningún efecto mágico   Comportamiento integrado para fusionar estilo y className por la misma razón   no tenemos esa característica de transferPropsTo. Nadie está seguro de qué   exactamente la lista completa de cosas mágicas son, lo que lo hace   difícil de razonar sobre el código y difícil de reutilizar cuando el estilo   tiene una firma diferente (por ejemplo, en el React Native próximo).

React.cloneElement es casi equivalente a:

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

Sin embargo, a diferencia de JSX y cloneWithProps, también conserva referencias. Esta   significa que si tienes un niño con una referencia, no accidentalmente   robarlo de tu antepasado. Obtendrá la misma referencia adjunta a   tu nuevo elemento.

Un patrón común es mapear a sus hijos y agregar un nuevo accesorio.   Se informaron muchos problemas sobre la pérdida de la referencia de cloneWithProps,   lo que hace más difícil razonar sobre su código. Ahora siguiendo el mismo   patrón con cloneElement funcionará como se espera. Por ejemplo:

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

Nota: React.cloneElement (child, {ref: 'newRef'}) Sustituye al   ref. por lo que aún no es posible que dos padres tengan una referencia al   mismo niño, a menos que use callback-refs.

Esta fue una característica fundamental para entrar en React 0.13 ya que los accesorios son ahora   inmutable. La ruta de actualización suele clonar el elemento, pero   al hacerlo podrías perder la ref. Por lo tanto, necesitábamos una mejor actualización   camino aquí. Al actualizar las listas de llamadas en Facebook, nos dimos cuenta de que   necesitábamos este método Recibimos los mismos comentarios de la comunidad.   Por lo tanto, decidimos hacer otro RC antes del lanzamiento final a   asegúrese de obtener esto

Planeamos eventualmente depreciar React.addons.cloneWithProps. No eran   hacerlo todavía, pero esta es una buena oportunidad para empezar a pensar   sus propios usos y considere usar React.cloneElement en su lugar. Bien ser   Asegúrese de enviar un lanzamiento con avisos de desaprobación antes de que en realidad   eliminarlo por lo que no es necesaria una acción inmediata.

Más aquí...


15
2018-06-12 07:03



Con la actualización a Reaccionar 16.3 ahora puedes usar React.createContext.

import * as React from 'react';

// React.createContext accepts a defaultValue as the first param
const { Provider: ParentProvider, Consumer: ChildConsumer } = React.createContext(); 

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

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

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


// Example of using Parent and Child

import * as React from 'react';

class SomeComponent extends React.Component {

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

React.createContext brilla donde React.cloneElement caso no pudo manejar componentes anidados

class SomeComponent extends React.Component {

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

10
2018-04-09 06:02



Necesitaba corregir la respuesta aceptada anteriormente para que funcione ese en lugar de esta puntero. Esta dentro del alcance de la función de mapa no tenía hacer algo función definida.

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

Actualización: esta corrección es para ECMAScript 5, en ES6 no es necesario var that = this


5
2017-12-29 20:05



Manera más limpia teniendo en cuenta uno o más niños

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

5
2017-11-24 09:24



Ya no necesitas {this.props.children}. Ahora puede envolver su componente hijo usando render en Route y pasa tus accesorios como de costumbre:

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

4
2017-11-02 11:59