Cara meneruskan alat peraga ke {this.props.children}


889

Saya mencoba menemukan cara yang tepat untuk mendefinisikan beberapa komponen yang dapat digunakan secara umum:

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

Ada logika yang terjadi untuk rendering antara komponen orang tua dan anak-anak, Anda bisa bayangkan <select>dan<option> sebagai contoh dari logika ini.

Ini adalah implementasi tiruan untuk tujuan pertanyaan:

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

Pertanyaannya adalah kapan pun Anda menggunakan {this.props.children}untuk mendefinisikan komponen pembungkus, bagaimana Anda mewariskan beberapa properti untuk semua anak-anaknya?


Jawaban:


955

Mengkloning anak-anak dengan alat peraga baru

Anda dapat menggunakan React.Children untuk beralih ke anak-anak, dan kemudian mengkloning setiap elemen dengan alat peraga baru (dangkal digabung) menggunakan React.cloneElement misalnya:

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

Fiddle: https://jsfiddle.net/2q294y43/2/

Memanggil anak-anak sebagai fungsi

Anda juga dapat memberikan alat peraga kepada anak-anak dengan alat bantu render . Dalam pendekatan ini anak-anak (yang dapat childrenatau nama prop lainnya) adalah fungsi yang dapat menerima argumen apa pun yang ingin Anda sampaikan dan mengembalikan anak-anak:

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

Alih-alih <React.Fragment>atau hanya<> Anda juga dapat mengembalikan array jika Anda inginkan.

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


7
Ini tidak berhasil untuk saya. ini tidak didefinisikan dalam React.cloneElement ()
Patrick

12
Jawaban ini tidak berhasil, yang valuediteruskan ke doSomethinghilang.
Dave

3
@DominicTobias Arg, maaf, saya beralih console.log untuk mengingatkan dan lupa untuk menggabungkan kedua params menjadi satu string.
Dave

1
Jawaban ini sangat membantu, tetapi saya mengalami masalah yang tidak disebutkan di sini dan saya bertanya-tanya apakah ada hal baru yang berubah atau apakah ada sesuatu yang aneh pada saya. Ketika saya mengkloning elemen anak saya, itu anak diatur ke elemen yang lama, sampai saya menambahkan this.props.children.props.children ke argumen ketiga cloneElement.
aphenine

7
Bagaimana jika anak dimuat melalui rute (v4) yang dimuat dari halaman rute terpisah?
blamb

394

Untuk cara yang sedikit lebih bersih untuk melakukannya, cobalah:

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

Sunting: Untuk digunakan dengan banyak anak individual (anak itu sendiri harus menjadi komponen) yang dapat Anda lakukan. Diuji dalam 16.8.6

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

10
Saya menggunakan jawaban yang paling banyak dinilai, tetapi yang ini jauh lebih lurus ke depan! Solusi ini juga apa yang mereka gunakan pada halaman contoh-contoh router reaksi.
captDaylight

10
Bisakah seseorang menjelaskan cara kerjanya (atau apa yang sebenarnya dilakukannya)? Membaca dokumen , saya tidak bisa melihat bagaimana ini akan turun ke anak-anak dan menambahkan penyangga itu untuk setiap anak - apakah itu yang seharusnya dilakukan? Jika ya, bagaimana kita tahu bahwa ini yang akan dilakukan? Sama sekali tidak jelas bahwa itu bahkan valid untuk melewatkan struktur data buram ( this.props.children) ke cloneElement... yang mengharapkan ... elemen.
GreenAsJade

51
Tepatnya, ini sepertinya tidak bekerja dengan lebih dari satu anak.
Danita

17
Jadi, Anda dapat menulis kode yang berfungsi saat seseorang memasukkan hanya satu anak ke sebuah komponen, tetapi ketika mereka menambahkan yang lain, itu mogok ... itu tidak terdengar bagus pada nilai nominal? Tampaknya menjadi jebakan bagi OP, yang bertanya secara khusus tentang memberikan alat peraga kepada semua anak.
GreenAsJade

10
@GreenAsJade tidak masalah selama komponen Anda mengharapkan anak tunggal. Anda dapat menentukan melalui propTypes komponen Anda bahwa ia mengharapkan satu anak. React.Children.onlyfungsi mengembalikan satu-satunya anak atau melempar pengecualian jika ada beberapa (ini tidak akan ada jika tidak ada use case).
cchamberlain

80

Coba ini

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

Itu bekerja untuk saya menggunakan react-15.1.


3
Apakah mungkin untuk kembali React.cloneElement()secara langsung tanpa mengelilinginya dalam <div>tag? Karena bagaimana jika anak itu adalah <span>(atau sesuatu yang lain) dan kami ingin mempertahankan tipe elemen tag-nya?
adrianmc

1
Jika satu anak Anda dapat meninggalkan bungkusnya dan solusi ini hanya berfungsi untuk satu anak jadi ya.
ThaJay

1
Bekerja untukku. Tanpa menyertakan <div> tidak apa-apa.
Crash Override

4
Jika Anda perlu menegakkan secara eksplisit bahwa Anda hanya menerima satu anak, Anda dapat melakukan React.cloneElement(React.Children.only(this.props.children), {...this.props})yang akan menimbulkan kesalahan jika diteruskan lebih dari satu anak. Maka Anda tidak perlu membungkusnya dengan div.
itsananderson

1
Jawaban ini dapat menghasilkan TypeError: nilai objek siklik. Kecuali jika Anda ingin salah satu alat peraga anak itu menjadi miliknya sendiri, gunakan let {children, ...acyclicalProps} = this.propskemudian React.cloneElement(React.Children.only(children), acyclicalProps).
Parabolord

69

Lewati alat peraga untuk mengarahkan anak-anak.

Lihat semua jawaban lain

Lulus berbagi, data global melalui pohon komponen melalui konteks

Konteks dirancang untuk berbagi data yang dapat dianggap “global” untuk struktur komponen React, seperti pengguna terotentikasi saat ini, tema, atau bahasa yang disukai. 1

Penafian: Ini adalah jawaban yang diperbarui, yang sebelumnya menggunakan API konteks lama

Ini didasarkan pada prinsip Konsumen / Menyediakan. Pertama, buat konteks Anda

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

Kemudian gunakan via

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

dan

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

Semua Konsumen yang merupakan keturunan Penyedia akan memberikan kembali setiap kali prop nilai Penyedia berubah. Propagasi dari Penyedia ke Konsumen turunannya tidak tunduk pada metode shouldComponentUpdate, sehingga Konsumen diperbarui bahkan ketika komponen leluhur menebus pembaruan tersebut. 1

Contoh lengkap, kode semi-pseudo.

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


6
Tidak seperti jawaban yang diterima ini akan bekerja dengan baik bahkan ketika ada elemen lain yang termasuk dalam Induk. Ini jelas merupakan jawaban terbaik.
Zaptree

6
Alat peraga! = Konteks
Petr Peller

Anda tidak dapat bergantung pada perubahan yang disebarkan melalui konteks. Gunakan alat peraga bila memungkinkan mereka berubah.
ThaJay

1
Mungkin saya tidak mengerti tetapi tidak salah untuk mengatakan "konteks membuat alat peraga tersedia"? Ketika saya terakhir menggunakan konteks, itu adalah hal yang terpisah (yaitu this.context) - itu tidak secara ajaib menggabungkan konteks dengan alat peraga. Anda harus sengaja mengatur dan menggunakan konteksnya, yang merupakan hal lain.
Josh

Anda mengerti dengan sempurna, itu tidak benar. Saya sudah mengedit jawaban saya.
Lyubomir

48

Melewati Alat Peraga untuk Anak-Anak yang Bersarang

Dengan pembaruan ke Bereaksi 16.6 Anda sekarang dapat menggunakan React.createContext dan 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 bersinar di mana case React.cloneElement tidak bisa menangani komponen bersarang

class SomeComponent extends React.Component {

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

3
Bisakah Anda jelaskan mengapa fungsi => adalah praktik yang buruk? Fungsi => membantu mengikat event handler untuk mendapatkan thiskonteks
Kenneth Truong

@KennethTruong karena setiap kali ditampilkan akan membuat fungsi.
berfungsi

9
@ itu tidak bekerja itu tidak benar. Itu hanya menciptakan fungsi baru ketika kelas dibuat. Ini tidak dibuat selama fungsi render ..
Kenneth Truong

@KennethTruong reactjs.org/docs/faq-functions.html#arrow-function-in-render saya pikir Anda berbicara tentang fungsi panah di render.
berfungsi

24

Anda dapat menggunakan React.cloneElement, lebih baik untuk mengetahui cara kerjanya sebelum Anda mulai menggunakannya di aplikasi Anda. Itu diperkenalkan di React v0.13, baca terus untuk informasi lebih lanjut, jadi sesuatu bersama ini bekerja untuk Anda:

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

Jadi bawalah baris-baris dari React dokumentasi agar Anda memahami bagaimana semuanya bekerja dan bagaimana Anda dapat menggunakannya:

Di React v0.13 RC2 kami akan memperkenalkan API baru, mirip dengan React.addons.cloneWithProps, dengan tanda tangan ini:

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

Tidak seperti cloneWithProps, fungsi baru ini tidak memiliki perilaku bawaan sihir untuk menggabungkan gaya dan className karena alasan yang sama kami tidak memiliki fitur itu dari transferPropsTo. Tidak ada yang yakin apa sebenarnya daftar lengkap hal-hal ajaib, yang membuatnya sulit untuk berpikir tentang kode dan sulit untuk digunakan kembali ketika gaya memiliki tanda tangan yang berbeda (misalnya dalam React Native yang akan datang).

React.cloneElement hampir setara dengan:

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

Namun, tidak seperti JSX dan cloneWithProps, ia juga mempertahankan referensi. Ini berarti bahwa jika Anda mendapatkan seorang anak dengan ref di atasnya, Anda tidak akan sengaja mencurinya dari leluhur Anda. Anda akan mendapatkan referensi yang sama dengan elemen baru Anda.

Satu pola umum adalah memetakan anak-anak Anda dan menambahkan properti baru. Ada banyak masalah yang dilaporkan tentang cloneWithProps kehilangan wasit, membuatnya lebih sulit untuk alasan tentang kode Anda. Sekarang mengikuti pola yang sama dengan cloneElement akan berfungsi seperti yang diharapkan. Sebagai contoh:

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

Catatan: React.cloneElement (child, {ref: 'newRef'}) TIDAK menimpa ref sehingga masih tidak mungkin bagi dua orang tua untuk memiliki ref untuk anak yang sama, kecuali jika Anda menggunakan ref-callback.

Ini adalah fitur penting untuk masuk ke React 0,13 karena alat peraga sekarang tidak dapat diubah. Jalur peningkatan seringkali untuk mengkloning elemen, tetapi dengan melakukan itu Anda mungkin kehilangan referensi. Oleh karena itu, kami memerlukan jalur peningkatan yang lebih baik di sini. Ketika kami meningkatkan callites di Facebook, kami menyadari bahwa kami membutuhkan metode ini. Kami mendapat umpan balik yang sama dari komunitas. Karena itu kami memutuskan untuk membuat RC lain sebelum rilis final untuk memastikan kami mendapatkannya.

Kami berencana untuk akhirnya mencabut React.addons.cloneWithProps. Kami belum melakukannya, tetapi ini adalah kesempatan yang baik untuk mulai berpikir tentang penggunaan Anda sendiri dan pertimbangkan untuk menggunakan React.cloneElement sebagai gantinya. Kami pasti akan mengirimkan rilis dengan pemberitahuan penghentian sebelum kami benar-benar menghapusnya sehingga tidak ada tindakan segera diperlukan.

lebih lanjut di sini ...


18

Cara terbaik, yang memungkinkan Anda melakukan transfer properti childrenseperti fungsi

Contoh:

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

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

1
Bagi saya ini tampaknya lebih mudah (dan lebih baik untuk kinerja?) Daripada jawaban yang diterima.
Shikyo

2
Ini mengharuskan anak-anak untuk berfungsi dan tidak berfungsi untuk komponen yang tersarang
ilusi digital

@ digitalitalusion, saya tidak mengerti apa artinya nested components. Bereaksi tidak memiliki pola bersarang, hanya komposisi. Ya, anak-anak harus menjadi fungsi, tidak ada konflik, karena ini anak JSX yang valid. Bisakah Anda memberi contoh nesting components?
Nick Ovchinnikov

1
Anda benar kasus anak-anak yang bersarang dalam dapat ditangani juga <Parent>{props => <Nest><ChildComponent /></Nest>}</Parent>alih-alih (tidak bekerja) <Parent><Nest>{props => <ChildComponent />}</Nest></Parent>jadi saya setuju ini adalah jawaban terbaik
ilusi digital

Saat mencoba, saya menerima yang berikut:TypeError: children is not a function
Ryan Prentiss

6

Saya perlu memperbaiki jawaban yang diterima di atas untuk membuatnya berfungsi dengan menggunakan itu daripada pointer ini . Ini dalam lingkup fungsi peta tidak memiliki fungsi doSomething yang ditentukan.

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

Pembaruan: perbaikan ini untuk ECMAScript 5, di ES6 tidak perlu dalam var yang = ini


13
atau cukup gunakanbind()
plus-

1
atau menggunakan fungsi panah yang mengikat ke lingkup leksikal, saya memperbarui jawaban saya
Dominic

bagaimana jika doSomethingmengambil objek, seperti doSomething: function(obj) { console.log(obj) }dan di dalam Child Anda akan dipanggil this.props.doSomething(obj)untuk logout"obj"
conor909

4
@ plus- saya tahu ini sudah lama, tetapi menggunakan bind di sini adalah ide yang buruk, bind menciptakan fungsi baru yang mengikat konteks ke yang baru. pada dasarnya fungsi memanggil applymetode. menggunakan bind()dalam fungsi render akan membuat fungsi baru setiap kali metode render dipanggil.
Bamieh

6

Cara bersih mempertimbangkan satu atau lebih anak

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

Yang ini tidak berfungsi untuk saya, itu memberikan kesalahan: anak-anak tidak didefinisikan.
Deelux

@Deelux this.props.children bukan anak
Martin Dawson

ini melewati anak sebagai anak sendiri di this.props. Secara umum saya hanya merekomendasikan kloning dengan alat peraga tertentu, bukan seluruh shebang.
Andy

Lulus {...this.props}tidak bekerja untuk saya, apakah jalannya {...child.props}benar?
Felipe Augusto

Untuk komponen fungsional:React.Children.map(children, child => React.cloneElement(child, props))
vsync

5

Tidak ada jawaban yang membahas masalah memiliki anak yang TIDAK Bereaksi komponen, seperti string teks. Penanganannya bisa seperti ini:

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

}

5

Anda tidak lagi membutuhkan {this.props.children}. Sekarang Anda dapat membungkus komponen anak Anda menggunakan renderdi Routedan lulus alat peraga seperti biasa:

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

2
Render props sekarang menjadi standar dalam React ( reactjs.org/docs/render-props.html ) dan layak dipertimbangkan sebagai jawaban yang diterima baru untuk pertanyaan ini.
Ian Danforth

19
Bagaimana ini merupakan jawaban untuk pertanyaan itu?
Maximo Dominguez

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

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

lihat contoh di sini: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview



4

Jika Anda memiliki banyak anak yang ingin Anda sampaikan alat peraga , Anda dapat melakukannya dengan cara ini, menggunakan React.Children.map:

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

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

Jika komponen Anda hanya memiliki satu anak, tidak perlu pemetaan, Anda bisa langsung cloneElement:

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

3

Menurut dokumentasi cloneElement()

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

Mengkloning dan mengembalikan elemen Bereaksi baru menggunakan elemen sebagai titik awal. Elemen yang dihasilkan akan memiliki properti elemen asli dengan properti baru digabung menjadi dangkal. Anak baru akan menggantikan anak yang sudah ada. kunci dan ref dari elemen asli akan dipertahankan.

React.cloneElement() hampir setara dengan:

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

Namun, itu juga melindungi referensi. Ini berarti bahwa jika Anda mendapatkan seorang anak dengan ref di atasnya, Anda tidak akan sengaja mencurinya dari leluhur Anda. Anda akan mendapatkan referensi yang sama dengan elemen baru Anda.

Jadi cloneElement adalah apa yang akan Anda gunakan untuk menyediakan alat peraga khusus untuk anak-anak. Namun ada beberapa anak dalam komponen dan Anda harus mengulanginya. Apa jawaban lain yang disarankan agar Anda memetakannya menggunakan React.Children.map. Namun React.Children.maptidak seperti React.cloneElementperubahan kunci dari elemen ditambahkan dan tambahan .$sebagai awalan. Periksa pertanyaan ini untuk detail lebih lanjut: React.cloneElement di dalam React.Children.map menyebabkan kunci elemen berubah

Jika Anda ingin menghindarinya, Anda harus memilih forEachfungsi seperti

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

}

2

Lebih jauh ke jawaban @and_rest, ini adalah bagaimana saya mengkloning anak-anak dan menambahkan kelas.

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

2

Saya pikir render prop adalah cara yang tepat untuk menangani skenario ini

Anda membiarkan Orang Tua menyediakan alat peraga yang diperlukan yang digunakan dalam komponen anak, dengan refactoring kode Orang Tua untuk melihat sesuatu seperti ini:

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

  return children({ doSomething })
}

Kemudian di Komponen anak Anda dapat mengakses fungsi yang disediakan oleh orang tua dengan cara ini:

class Child extends React {

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

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

}

Sekarang struktur fianl akan terlihat seperti ini:

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

bagaimana jika orang tua hanyalah pembungkus untuk {children} yang merupakan textarea di komponen kelas lain?
Adebayo

2

Metode 1 - mengkloning anak-anak

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

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

Metode 2 - gunakan konteks yang dapat disusun

Konteks memungkinkan Anda untuk meneruskan penyangga ke komponen anak dalam tanpa secara eksplisit meneruskannya sebagai penyangga melalui komponen di antaranya.

Konteks dilengkapi dengan kelemahan:

  1. Data tidak mengalir secara reguler - melalui alat peraga.
  2. Menggunakan konteks menciptakan kontrak antara konsumen dan penyedia. Mungkin lebih sulit untuk memahami dan mereplikasi persyaratan yang diperlukan untuk menggunakan kembali suatu komponen.

Menggunakan konteks yang dapat dikomposisikan

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

Agak terlambat, tetapi bisakah Anda menjelaskan notasinya {children}:{children:ReactNode}?
camille

@ Camille, Ini adalah jenis huruf. Melihat itu sekarang, saya hanya akan menjawab dengan Javascript, dan bahkan jika saya akan menulis naskah, saya akan melakukannya secara berbeda. Dapat mengeditnya di masa mendatang.
Ben Carp

1
@camille, pada dasarnya itu berarti bahwa nilai yang memiliki kunci "children"bertipeReactNode
Ben Carp

1

Cara paling licin untuk melakukan ini:

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

5
Apakah ini tidak menyalin this.props.children ke this.props.children anak? dan pada dasarnya menyalin anak itu ke dalam dirinya sendiri?
Arshabh Agarwal

1

Untuk siapa pun yang memiliki elemen anak tunggal ini harus melakukannya.

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

0

Apakah ini yang Anda butuhkan?

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

4
Tidak, saya tidak ingin membatasi konten pembungkus saya dengan beberapa konten tertentu.
plus-

0

Beberapa alasan React.children tidak bekerja untuk saya Inilah yang bekerja untuk saya.

Saya hanya ingin menambahkan kelas untuk anak itu. mirip dengan mengubah prop

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

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

Kuncinya di sini adalah React.cloneElement . Anda dapat melewati prop apa pun dengan cara yang sama


0

Render props adalah pendekatan paling akurat untuk masalah ini. Alih-alih meneruskan komponen anak ke komponen induk sebagai alat bantu anak-anak, biarkan orang tua merender komponen anak secara manual. Render adalah alat peraga bawaan yang bereaksi, yang mengambil parameter fungsi. Dalam fungsi ini Anda dapat membiarkan komponen induk merender apa pun yang Anda inginkan dengan parameter khusus. Pada dasarnya ia melakukan hal yang sama seperti alat peraga anak tetapi lebih dapat disesuaikan.

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

Contoh di codepen


0

Saat menggunakan komponen fungsional, Anda akan sering mendapatkan TypeError: Cannot add property myNewProp, object is not extensiblekesalahan saat mencoba mengaktifkan properti baru props.children. Ada pekerjaan sekitar untuk ini dengan mengkloning alat peraga dan kemudian mengkloning anak itu sendiri dengan alat peraga baru.

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>
  )
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.