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

889

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

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

Hay una lógica en curso para la representación entre los componentes principales y secundarios, por supuesto, se puede 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 cada vez que se utiliza {this.props.children}para definir un componente de envoltura, ¿cómo se transfiere alguna propiedad a todos sus elementos secundarios?

más-
fuente

Respuestas:

955

Clonando niños con nuevos accesorios

Puede usar React.Children para iterar sobre los hijos y luego clonar cada elemento con nuevos accesorios (fusión superficial) usando React.cloneElement, por ejemplo:

import React, { Children, isValidElement, cloneElement } from 'react';

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

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    const childrenWithProps = Children.map(children, child => {
      // Checking isValidElement is the safe way and avoids a TS error too.
      if (isValidElement(child)) {
        return cloneElement(child, { doSomething })
      }

      return child;
    });

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

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

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

Llamar a los niños como una función

También puede pasar accesorios a niños con accesorios de render . En este enfoque, los elementos secundarios (que pueden ser childreno cualquier otro nombre de apoyo) es una función que puede aceptar cualquier argumento que desee pasar y devuelve los elementos secundarios:

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

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    // Note that children is called as a function and we can pass args to it
    return <div>{children(doSomething)}</div>
  }
};

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

En lugar de <React.Fragment>o simplemente <>también puede devolver una matriz si lo prefiere.

Fiddle: https://jsfiddle.net/ferahl/y5pcua68/7/

Dominic
fuente
77
Esto no funciona para mi. esto no está definido en React.cloneElement ()
Patrick
12
Esta respuesta no funciona, se pierde el valuepasado doSomething.
Dave
3
@DominicTobias Arg, lo siento, cambié console.log para alertar y olvidé conectar los dos params a una sola cadena.
Dave
1
Esta respuesta fue muy útil, pero me encontré con un problema que no se menciona aquí y me preguntaba si algo nuevo ha cambiado o si es algo extraño de mi parte. Cuando cloné mi elemento hijo, su hijo se configuró en el elemento antiguo, hasta que agregué this.props.children.props.children al tercer argumento de cloneElement.
afhenine
77
¿Qué sucede si el niño se carga a través de una ruta (v4) que se carga desde una página de ruta separada?
blamb
394

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

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

Editar: para usar con varios elementos secundarios individuales (el elemento secundario debe ser un componente) puede hacerlo. Probado en 16.8.6

<div>
    {React.cloneElement(props.children[0], { loggedIn: true, testingTwo: true })}
    {React.cloneElement(props.children[1], { loggedIn: true, testProp: false })}
</div>
Andres F Garcia
fuente
10
Estaba usando la respuesta más valorada, ¡pero esta es mucho más sencilla! Esta solución también es lo que usan en la página de ejemplos de react-router.
captDaylight
10
¿Alguien podría explicar cómo funciona esto (o qué hace realmente)? Al leer los documentos , no pude ver cómo esto descendería a los niños y agregaría ese accesorio a cada niño, ¿es eso lo que se pretende hacer? Si es así, ¿cómo sabemos que esto es lo que hará? No es nada obvio que incluso sea válido pasar una estructura de datos opaca ( this.props.children) a cloneElement... que espera un ... elemento.
GreenAsJade
51
Exactamente, esto no parece funcionar con más de un niño.
Danita
17
Por lo tanto, puede escribir código que funcione mientras alguien pasa a un solo niño a un componente, pero cuando agrega otro, se bloquea ... ¿eso no suena muy bien en su valor nominal? Parecería ser una trampa para el OP, que preguntó específicamente sobre pasar accesorios a todos los niños.
GreenAsJade
10
@GreenAsJade está bien siempre que su componente esté esperando un solo hijo. Puede definir a través de sus componentes propTypes que espera un solo hijo. React.Children.onlyLa función devuelve el único hijo o arroja una excepción si hay múltiples (esto no existiría si no hubiera un caso de uso).
cchamberlain
80

Prueba esto

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

Funcionó para mí usando react-15.1.

7puns
fuente
3
¿Es posible regresar React.cloneElement()directamente sin rodearlo en las <div>etiquetas? Porque, ¿qué pasa si el niño es un <span>(o algo más) y queremos preservar su tipo de elemento de etiqueta?
adrianmc
1
Si es un niño, puede omitir el envoltorio y esta solución solo funciona para un niño, así que sí.
ThaJay
1
Funciona para mi. Sin encerrar <div> está bien.
Crash Override
44
Si necesita hacer cumplir explícitamente que solo recibe un hijo, puede hacer React.cloneElement(React.Children.only(this.props.children), {...this.props})lo que arrojará un error si se pasa más de un hijo. Entonces no necesitas envolver en un div.
itsananderson
1
Esta respuesta puede producir un TypeError: valor de objeto cíclico. A menos que desee que uno de los accesorios del niño sea él mismo, use let {children, ...acyclicalProps} = this.propsy luego React.cloneElement(React.Children.only(children), acyclicalProps).
Parabolord
69

Pase accesorios para dirigir a los niños.

Ver todas las otras respuestas

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

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

Descargo de responsabilidad: esta es una respuesta actualizada, la anterior utilizaba la antigua API de contexto

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

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

Luego use 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 se volverán a procesar cada vez que cambie el valor de apoyo del proveedor. La propagación del Proveedor a sus Consumidores descendientes no está sujeta al método shouldComponentUpdate, por lo que el Consumidor se actualiza incluso cuando un componente ancestro abandona la actualización. 1

Ejemplo completo, semi-pseudocó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

Lyubomir
fuente
66
A diferencia de la respuesta aceptada, esto funcionará correctamente incluso cuando haya otros elementos incluidos en Parent. Esta es definitivamente la mejor respuesta.
Zaptree
66
Props! = Context
Petr Peller
no puede depender de los cambios que se propagan a través del contexto. Usa accesorios cuando sea posible que cambien.
ThaJay
1
Tal vez no entiendo, pero ¿no está mal decir "el contexto hace que los accesorios estén disponibles"? La última vez que utilicé el contexto, era algo diferente (es decir this.context): no fusionaba mágicamente el contexto con accesorios. Tenías que establecer y usar intencionalmente el contexto, que es otra cosa.
Josh
Entiendes perfectamente, era incorrecto. He editado mi respuesta.
Lyubomir
48

Pasando accesorios a niños anidados

Con la actualización a Reaccionar 16.6 ahora puede usar React.createContext y contextType .

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.createContext brilla donde el caso React.cloneElement no puede manejar componentes anidados

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}
Kenneth Truong
fuente
3
¿Puedes explicar por qué => las funciones son una mala práctica? La función => ayuda a vincular los controladores de eventos para obtener thiscontexto
Kenneth Truong
@KennethTruong porque cada vez que se renderiza crea una función.
itdoesntwork
99
@itdoesntwork eso no es cierto. Solo crea una nueva función cuando se crea la clase. No se crea durante la función de renderizado ..
Kenneth Truong
@KennethTruong reactjs.org/docs/faq-functions.html#arrow-function-in-render Pensé que estabas hablando de la función de flecha en render.
itdoesntwork
24

Puede usar React.cloneElement, es mejor saber cómo funciona antes de comenzar a usarlo en su aplicación. Se presenta en React v0.13, sigue leyendo para obtener más información, así que algo junto con este trabajo 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 funciona todo y cómo puede utilizarlas:

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 comportamiento mágico incorporado para fusionar estilo y className por la misma razón por la que no tenemos esa característica de transferPropsTo. Nadie está seguro de cuál es exactamente la lista completa de cosas mágicas, lo que dificulta razonar sobre el código y es difícil de reutilizar cuando el estilo tiene una firma diferente (por ejemplo, en el próximo React Native).

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 las referencias. Esto significa que si obtienes un niño con un árbitro, no lo robarás accidentalmente de tu antepasado. Obtendrá la misma referencia adjunta a su nuevo elemento.

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

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

Nota: React.

Esta fue una característica crítica para entrar en React 0.13 ya que los accesorios ahora son inmutables. La ruta de actualización a menudo es para clonar el elemento, pero al hacerlo puede perder la referencia. Por lo tanto, necesitábamos una mejor ruta de actualización aquí. Cuando estábamos actualizando los sitios 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 para asegurarnos de que lo logremos.

Planeamos desaprobar eventualmente React.addons.cloneWithProps. Todavía no lo estamos haciendo, pero esta es una buena oportunidad para comenzar a pensar en sus propios usos y considerar usar React.cloneElement en su lugar. Nos aseguraremos de enviar un lanzamiento con avisos de desaprobación antes de que realmente lo eliminemos, por lo que no es necesaria ninguna acción inmediata.

más aquí ...

Alireza
fuente
18

La mejor manera, que le permite hacer la transferencia de propiedad es childrencomo una función

Ejemplo:

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

export const Parent = ({ children }) => {
    const somePropsHere = { //...any }
    <>
        {children(somePropsHere)}
    </>
}
Nick Ovchinnikov
fuente
1
Esto me parece mucho más sencillo (¿y mejor para el rendimiento?) Que la respuesta aceptada.
Shikyo
2
Esto requiere que los niños sean una función y no funciona para componentes deply anidados
ilusión digital
@digitalillusion, no entiendo qué significa nested components. React no tiene patrones anidados, solo composiciones. Sí, los hijos deben ser una función, no hay conflictos, porque este es un hijo JSX válido. ¿Puedes dar un ejemplo con nesting components?
Nick Ovchinnikov
1
Tienes razón, el caso de los niños profundamente anidados también se puede manejar en <Parent>{props => <Nest><ChildComponent /></Nest>}</Parent>lugar de (no funciona), <Parent><Nest>{props => <ChildComponent />}</Nest></Parent>así que estoy de acuerdo en que esta es la mejor respuesta
ilusión digital
Cuando intento, recibo lo siguiente:TypeError: children is not a function
Ryan Prentiss
6

Necesitaba corregir la respuesta aceptada arriba para que funcione usando eso en lugar de este puntero. Esta dentro del alcance de la función de mapa no tenía definida la función 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>
}})

Actualización: esta solución es para ECMAScript 5, en ES6 no hay necesidad en var que = esto

olenak
fuente
13
o simplemente usebind()
plus-
1
o use una función de flecha que se une al alcance léxico, actualicé mi respuesta
Dominic
¿y si doSomethingtomó un objeto, como doSomething: function(obj) { console.log(obj) }y en el Niño que se dice this.props.doSomething(obj)para cerrar la sesión"obj"
conor909
44
@ plus- sé que esto es antiguo, pero usar bind aquí es una idea terrible, bind crea una nueva función que une el contexto a una nueva. básicamente una función que llama al applymétodo. usar bind()en la función de renderizado creará una nueva función cada vez que se llame al método de renderizado.
Bamieh
6

Manera más limpia considerando uno o más niños

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
y descansar
fuente
Este no funciona para mí, da un error: niños no definidos.
Deelux el
@Deelux this.props.children en lugar de niños
Martin Dawson
esto pasa al niño como sus propios hijos this.props. En general, solo recomendaría clonar con accesorios específicos, no todo el asunto.
Andy
Pasar {...this.props}no funcionó para mí, ¿es la forma {...child.props}correcta?
Felipe Augusto el
Para los componentes funcionales:React.Children.map(children, child => React.cloneElement(child, props))
VSYNC
5

Ninguna de las respuestas aborda el problema de tener hijos que NO sean componentes React, como cadenas de texto. Una solución alternativa podría ser algo como esto:

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

}
poynting
fuente
5

Ya no lo necesitas {this.props.children}. Ahora usted puede envolver su componente secundario usando renderen Routey pasar sus puntales 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>
yeasayer
fuente
2
Los accesorios de render ahora son estándar en React ( reactjs.org/docs/render-props.html ) y vale la pena considerarlos como una nueva respuesta aceptada para esta pregunta.
Ian Danforth
19
¿Cómo es esta una respuesta a la pregunta?
Máximo Domínguez
4

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

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

ver ejemplo aquí: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview

Maksim Kostromin
fuente
4

Si tiene varios hijos a los que desea pasarles accesorios , puede hacerlo de esta manera, usando React.Children.map:

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

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

Si su componente tiene solo un hijo, no hay necesidad de mapear, simplemente puede clonarElement de inmediato:

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

De acuerdo con la documentación de cloneElement()

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

Clonar y devolver un nuevo elemento Reaccionar utilizando el elemento como punto de partida. El elemento resultante tendrá los accesorios del elemento original con los nuevos accesorios fusionados en forma superficial. Los nuevos niños reemplazarán a los niños existentes. La clave y la referencia del elemento original se conservarán.

React.cloneElement() es casi equivalente a:

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

Sin embargo, también conserva las referencias. Esto significa que si consigues un niño con un árbitro, no lo robarás accidentalmente de tu antepasado. Obtendrá la misma referencia adjunta a su nuevo elemento.

Entonces cloneElement es lo que usarías para proporcionar accesorios personalizados a los niños. Sin embargo, puede haber varios elementos secundarios en el componente y necesitaría recorrerlo. Lo que otras respuestas sugieren es que las mapees con ellas React.Children.map. Sin embargo, a React.Children.mapdiferencia de los React.cloneElementcambios, las teclas del elemento anexan y extra .$como prefijo. Consulte esta pregunta para más detalles: React.cloneElement dentro de React.Children.map está causando cambios en las claves de los elementos

Si desea evitarlo, debe optar por la forEachfunción como

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

}
Shubham Khatri
fuente
2

Además de la respuesta @and_rest, así es como clono a los niños y agrego una clase.

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

Creo que un accesorio de render es la forma adecuada de manejar este escenario

Dejas que el padre proporcione los accesorios necesarios utilizados en el componente hijo, refactorizando el código principal para que se vea de la siguiente manera:

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

  return children({ doSomething })
}

Luego, en el componente secundario, puede acceder a la función provista por el padre de esta manera:

class Child extends React {

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

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

}

Ahora la estructura final se verá así:

<Parent>
  {(doSomething) =>
   (<Fragment>
     <Child value="1" doSomething={doSomething}>
     <Child value="2" doSomething={doSomething}>
    <Fragment />
   )}
</Parent>
ZEE
fuente
¿Qué pasa si el padre es solo un contenedor para un {children} que es un área de texto en otro componente de la clase?
Adebayo
2

Método 1 - clonar niños

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

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

Método 2: usar contexto composable

El contexto le permite pasar un accesorio a un componente secundario profundo sin pasarlo explícitamente como accesorio a través de los componentes intermedios.

El contexto viene con inconvenientes:

  1. Los datos no fluyen de la manera regular, a través de accesorios.
  2. El uso del contexto crea un contrato entre el consumidor y el proveedor. Puede ser más difícil de entender y replicar los requisitos necesarios para reutilizar un componente.

Usando un contexto composable

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>;
};
Ben Carp
fuente
Un poco tarde, pero ¿podrías explicar la notación {children}:{children:ReactNode}?
camille
@camille, es una cosa mecanografiada. Mirándolo ahora, solo respondería con Javascript, e incluso si escribiera Typecript, lo haría de manera diferente. Podría editarlo en el futuro.
Ben Carp
1
@camille, básicamente significa que el valor que tiene la clave "children"es de tipoReactNode
Ben Carp
1

La forma más hábil de hacer esto:

    {React.cloneElement(this.props.children, this.props)}
nitte93user3232918
fuente
55
¿Esto no copia this.props.children en this.props.children del niño? y en efecto copiando al niño en sí mismo?
Arshabh Agarwal
1

Para cualquiera que tenga un solo elemento hijo, esto debería hacerlo.

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

¿Es esto lo que necesitabas?

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>
  }
})
amit_183
fuente
44
No, no quiero restringir el contenido de mi contenedor a un contenido específico.
más-
0

Por alguna razón, React.children no funcionaba para mí. Esto es lo que funcionó para mí.

Solo quería agregar una clase al niño. similar a cambiar un accesorio

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

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

El truco aquí es el React.cloneElement . Puedes pasar cualquier accesorio de manera similar

aWebDeveloper
fuente
0

Render props es el enfoque más preciso para este problema. En lugar de pasar el componente hijo al componente padre como accesorios hijos, deje que el padre represente el componente hijo manualmente. Render es accesorios integrados en react, que toma el parámetro de función. En esta función, puede permitir que el componente principal represente lo que desee con parámetros personalizados. Básicamente, hace lo mismo que los accesorios para niños, pero es más personalizable.

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

Ejemplo en codepen

omeralper
fuente
0

Cuando utilice componentes funcionales, a menudo obtendrá el TypeError: Cannot add property myNewProp, object is not extensibleerror cuando intente establecer nuevas propiedades props.children. Hay una solución para esto clonando los accesorios y luego clonando al niño con los nuevos accesorios.

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>
  )
}
un maestro
fuente