Cách truyền đạo cụ cho {this.props.children}


889

Tôi đang cố gắng tìm cách thích hợp để xác định một số thành phần có thể được sử dụng theo cách chung:

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

Tất nhiên, có một logic đang diễn ra giữa các thành phần cha mẹ và con cái, bạn có thể tưởng tượng <select><option>như một ví dụ về logic này.

Đây là một triển khai giả cho mục đích của câu hỏi:

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

Câu hỏi là bất cứ khi nào bạn sử dụng {this.props.children}để xác định một thành phần bao bọc, làm thế nào để bạn truyền lại một số tài sản cho tất cả các con của nó?


Câu trả lời:


955

Nhân bản trẻ em với đạo cụ mới

Bạn có thể sử dụng React.Children để lặp lại cho trẻ em và sau đó sao chép từng phần tử với các đạo cụ mới (được hợp nhất nông) bằng React.cloneEuity, ví dụ:

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

Câu đố: https://jsfiddle.net/2q294y43/2/

Gọi trẻ em là một chức năng

Bạn cũng có thể truyền đạo cụ cho trẻ em với đạo cụ render . Theo cách tiếp cận này, trẻ em (có thể childrenhoặc bất kỳ tên prop nào khác) là một chức năng có thể chấp nhận bất kỳ đối số nào bạn muốn vượt qua và trả về trẻ em:

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

Thay vì <React.Fragment>hoặc đơn giản, <>bạn cũng có thể trả về một mảng nếu bạn thích.

Câu đố: https://jsfiddle.net/ferahl/y5pcua68/7/


7
Điều này không làm việc cho tôi. điều này không được xác định trong React.cloneEuity ()
Patrick

12
Câu trả lời này không hoạt động, valuethông qua doSomethingbị mất.
Dave

3
@DominicTobias Arg, xin lỗi, tôi đã chuyển console.log sang cảnh báo và quên nối hai tham số thành một chuỗi.
Dave

1
Câu trả lời này rất hữu ích, nhưng tôi gặp phải một vấn đề không được đề cập ở đây và tôi tự hỏi liệu đó có phải là một điều mới đã thay đổi hay liệu đó có phải là điều kỳ lạ ở phía tôi không. Khi tôi nhân bản thành phần con của mình, con của nó được đặt thành phần tử cũ, cho đến khi tôi thêm this.props.children.props.children vào đối số thứ ba của cloneEuity.
aphenine

7
Điều gì sẽ xảy ra nếu đứa trẻ được tải thông qua một tuyến đường (v4) được tải từ một trang tuyến đường riêng biệt?
blamb

394

Để biết cách làm sạch hơn một chút, hãy thử:

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

Chỉnh sửa: Để sử dụng với nhiều trẻ em riêng lẻ (trẻ phải là một thành phần) bạn có thể làm. Đã thử nghiệm vào 16.8.6

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

10
Tôi đã sử dụng câu trả lời được đánh giá cao nhất, nhưng câu trả lời này là thẳng hơn nhiều! Giải pháp này cũng là những gì họ sử dụng trên trang ví dụ về bộ định tuyến phản ứng.
captDaylight

10
Ai đó có thể giải thích làm thế nào điều này hoạt động (hoặc những gì nó thực sự làm)? Đọc các tài liệu , tôi không thể thấy điều này sẽ xảy ra với trẻ em như thế nào và thêm chỗ dựa đó cho mỗi đứa trẻ - đó có phải là những gì nó dự định làm không? Nếu có, làm thế nào để chúng ta biết rằng đây là những gì nó sẽ làm? Hoàn toàn không rõ ràng rằng nó thậm chí còn hợp lệ để chuyển một cấu trúc dữ liệu mờ ( this.props.children) cho cloneElement... mà đang mong đợi một phần tử ....
GreenAsJade

51
Chính xác, điều này dường như không hoạt động với nhiều hơn một đứa trẻ.
Danita

17
Vì vậy, bạn có thể viết mã hoạt động trong khi ai đó chỉ chuyển một đứa trẻ vào một thành phần, nhưng khi chúng thêm một mã khác, nó bị hỏng ... điều đó nghe có vẻ không tốt trên mệnh giá? Nó dường như là một cái bẫy cho OP, người đã hỏi cụ thể về việc truyền đạo cụ cho tất cả trẻ em.
GreenAsJade

10
@GreenAsJade vẫn ổn miễn là thành phần của bạn đang mong đợi một đứa trẻ. Bạn có thể xác định thông qua các thành phần propTypes của mình mà nó mong đợi một đứa trẻ. React.Children.onlyHàm trả về con duy nhất hoặc ném ngoại lệ nếu có nhiều (điều này sẽ không tồn tại nếu không có trường hợp sử dụng).
cchamberlain

80

Thử cái này

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

Nó hoạt động với tôi bằng cách sử dụng Reac-15.1.


3
Có thể trả lại React.cloneElement()trực tiếp mà không bao quanh nó trong <div>thẻ? Bởi vì điều gì sẽ xảy ra nếu đứa trẻ là một <span>(hoặc một cái gì đó khác) và chúng tôi muốn duy trì loại phần tử thẻ của nó?
adrianmc

1
Nếu đó là một đứa trẻ, bạn có thể bỏ qua trình bao bọc và giải pháp này chỉ hiệu quả với một đứa trẻ nên có.
ThaJay

1
Làm việc cho tôi. Không kèm theo <div> là ok.
cố ghi đè

4
Nếu bạn cần thực thi một cách rõ ràng rằng bạn chỉ nhận được một đứa trẻ, bạn có thể làm điều React.cloneElement(React.Children.only(this.props.children), {...this.props})đó sẽ gây ra lỗi nếu nó được truyền nhiều hơn một đứa trẻ. Sau đó, bạn không cần phải bọc trong một div.
itananderson

1
Câu trả lời này có thể tạo ra giá trị đối tượng TypeError: cyclic. Trừ khi bạn muốn một trong những đạo cụ của trẻ là chính nó, hãy sử dụng let {children, ...acyclicalProps} = this.propsvà sau đó React.cloneElement(React.Children.only(children), acyclicalProps).
Parabolord

69

Truyền đạo cụ cho con trực tiếp.

Xem tất cả các câu trả lời khác

Truyền dữ liệu toàn cầu được chia sẻ thông qua cây thành phần thông qua ngữ cảnh

Bối cảnh được thiết kế để chia sẻ dữ liệu có thể được coi là thế giới toàn cầu cho một nhóm các thành phần React, chẳng hạn như người dùng, chủ đề hoặc ngôn ngữ ưa thích hiện tại. 1

Tuyên bố miễn trừ trách nhiệm: Đây là câu trả lời được cập nhật, câu trả lời trước đã sử dụng API ngữ cảnh cũ

Nó dựa trên nguyên tắc Tiêu dùng / Cung cấp. Đầu tiên, tạo bối cảnh của bạn

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

Sau đó sử dụng qua

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

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

Tất cả Người tiêu dùng là hậu duệ của Nhà cung cấp sẽ kết xuất lại bất cứ khi nào thay đổi giá trị của Nhà cung cấp thay đổi. Việc truyền bá từ Nhà cung cấp đến Người tiêu dùng hậu duệ của nó không phải tuân theo phương thức ShouldComponentUpdate, do đó, Người tiêu dùng được cập nhật ngay cả khi thành phần tổ tiên thoát khỏi bản cập nhật. 1

Ví dụ đầy đủ, mã bán giả.

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
Không giống như câu trả lời được chấp nhận, điều này sẽ hoạt động đúng ngay cả khi có các yếu tố khác được bao gồm trong Parent. Đây chắc chắn là câu trả lời tốt nhất.
Zaptree

6
Đạo cụ! = Bối cảnh
Petr Peller

bạn không thể phụ thuộc vào những thay đổi lan truyền qua ngữ cảnh. Sử dụng đạo cụ khi có thể chúng thay đổi.
ThaJay

1
Có thể tôi không hiểu nhưng không sai khi nói "bối cảnh làm cho đạo cụ có sẵn"? Khi tôi sử dụng bối cảnh lần cuối, đó là một điều riêng biệt (nghĩa là this.context) - nó đã không hợp nhất một cách kỳ diệu bối cảnh với các đạo cụ. Bạn phải cố ý thiết lập và sử dụng bối cảnh, đó là một điều hoàn toàn khác.
Josh

Bạn hiểu hoàn hảo, nó không chính xác. Tôi đã chỉnh sửa câu trả lời của mình.
Lyubomir

48

Truyền đạo cụ cho trẻ em làm tổ

Với bản cập nhật cho React 16.6, giờ đây bạn có thể sử dụng React.createContextbối cảnh .

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 tỏa sáng trong trường hợp React.cloneEuity không thể xử lý các thành phần lồng nhau

class SomeComponent extends React.Component {

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

3
Bạn có thể giải thích tại sao => chức năng là một thực tiễn xấu? Hàm => giúp liên kết các trình xử lý sự kiện để lấy thisbối cảnh
Kenneth Truong

@KennethTruong vì mỗi lần nó tái hiện nó sẽ tạo ra một hàm.
itdoesntwork

9
@itdoesntwork điều đó không đúng. Nó chỉ tạo một hàm mới khi lớp được tạo. Nó không được tạo trong chức năng kết xuất ..
Kenneth Truong

@KennethTruong Reacjs.org/docs/faq-fifts.html#arrow-feft-in-render tôi nghĩ rằng bạn đang nói về chức năng mũi tên trong kết xuất.
itdoesntwork

24

Bạn có thể sử dụng React.cloneElement, tốt hơn là nên biết nó hoạt động như thế nào trước khi bạn bắt đầu sử dụng nó trong ứng dụng của mình. Nó được giới thiệu React v0.13, đọc để biết thêm thông tin, vì vậy một cái gì đó cùng với công việc này cho bạn:

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

Vì vậy, hãy mang các dòng từ tài liệu React để bạn hiểu cách tất cả hoạt động và cách bạn có thể sử dụng chúng:

Trong React v0.13 RC2, chúng tôi sẽ giới thiệu một API mới, tương tự như React.addons.cloneWithProps, với chữ ký này:

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

Không giống như cloneWithProps, chức năng mới này không có bất kỳ hành vi tích hợp ma thuật nào để hợp nhất kiểu và className vì lý do tương tự chúng ta không có tính năng đó từ transferPropsTo. Không ai chắc chắn chính xác danh sách đầy đủ các thứ ma thuật là gì, điều này gây khó khăn cho việc suy luận về mã và khó sử dụng lại khi phong cách có một chữ ký khác (ví dụ như trong React Native sắp tới).

React.cloneEuity gần như tương đương với:

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

Tuy nhiên, không giống như JSX và cloneWithProps, nó cũng bảo toàn refs. Điều này có nghĩa là nếu bạn có một đứa trẻ với một giới thiệu về nó, bạn sẽ không vô tình đánh cắp nó từ tổ tiên của bạn. Bạn sẽ nhận được cùng một tham chiếu đến yếu tố mới của bạn.

Một mô hình phổ biến là lập bản đồ cho con cái của bạn và thêm một chỗ dựa mới. Có nhiều vấn đề được báo cáo về cloneWithProps mất ref, khiến cho việc lập luận về mã của bạn trở nên khó khăn hơn. Bây giờ theo cùng một mẫu với cloneEuity sẽ hoạt động như mong đợi. Ví dụ:

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

Lưu ý: React.cloneE bổ sung (con, {ref: 'newRef'}) KHÔNG ghi đè lên ref nên hai cha mẹ vẫn không thể có một ref cho cùng một đứa trẻ, trừ khi bạn sử dụng ref-refs.

Đây là một tính năng quan trọng để vào React 0.13 vì các đạo cụ giờ đây không thay đổi. Đường dẫn nâng cấp thường là sao chép phần tử, nhưng bằng cách đó bạn có thể mất ref. Do đó, chúng tôi cần một đường dẫn nâng cấp đẹp hơn ở đây. Khi chúng tôi nâng cấp các cuộc gọi tại Facebook, chúng tôi nhận ra rằng chúng tôi cần phương pháp này. Chúng tôi đã nhận được phản hồi tương tự từ cộng đồng. Do đó, chúng tôi đã quyết định tạo một RC khác trước khi phát hành cuối cùng để đảm bảo chúng tôi có được điều này.

Chúng tôi dự định cuối cùng sẽ phản đối React.addons.cloneWithProps. Chúng tôi chưa làm điều đó, nhưng đây là một cơ hội tốt để bắt đầu suy nghĩ về việc sử dụng của riêng bạn và xem xét sử dụng React.cloneEuity thay thế. Chúng tôi chắc chắn sẽ gửi một bản phát hành với các thông báo phản đối trước khi chúng tôi thực sự xóa nó để không cần phải hành động ngay lập tức.

thêm ở đây ...


18

Cách tốt nhất, cho phép bạn thực hiện chuyển nhượng tài sản là children giống như một chức năng

Thí dụ:

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

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

1
Điều này có vẻ đơn giản hơn (và tốt hơn cho hiệu suất?) Đối với tôi hơn là câu trả lời được chấp nhận.
Shikyo ngày

2
Điều này đòi hỏi trẻ em phải là một chức năng và không hoạt động đối với các thành phần lồng nhau
ảo ảnh kỹ thuật số

@digitalillusion, tôi không hiểu ý nghĩa của nó nested components. React không có các mẫu lồng nhau, chỉ có các tác phẩm. Đúng, trẻ em phải là một hàm, không có bất kỳ xung đột nào, bởi vì đây là đứa trẻ JSX hợp lệ. Bạn có thể cho một ví dụ với nesting components?
Nick Ovchinnikov

1
Bạn nói đúng trường hợp trẻ em làm tổ sâu cũng có thể được xử lý <Parent>{props => <Nest><ChildComponent /></Nest>}</Parent>thay vì (không hoạt động) <Parent><Nest>{props => <ChildComponent />}</Nest></Parent>vì vậy tôi đồng ý đây là câu trả lời tốt nhất
ảo ảnh kỹ thuật số

Khi thử, tôi nhận được những điều sau:TypeError: children is not a function
Ryan Prentiss

6

Tôi cần thiết để sửa chữa câu trả lời được chấp nhận ở trên để làm cho nó hoạt sử dụng thay vì này con trỏ. Điều này trong phạm vi chức năng bản đồ không có chức năng doS Something được xác định.

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

Cập nhật: bản sửa lỗi này dành cho ECMAScript 5, trong ES6 không cần trong var that = this


13
hoặc chỉ sử dụngbind()
plus-

1
hoặc sử dụng chức năng mũi tên liên kết với phạm vi từ vựng, tôi đã cập nhật câu trả lời của mình
Dominic

Điều gì xảy ra nếu doSomethinglấy một đối tượng, như doSomething: function(obj) { console.log(obj) }và trong Trẻ em mà bạn gọi this.props.doSomething(obj)để đăng xuất"obj"
conor909

4
@ plus- tôi biết điều này đã cũ, nhưng sử dụng liên kết ở đây là một ý tưởng tồi tệ, liên kết tạo ra một chức năng mới liên kết bối cảnh với một cái mới. về cơ bản là một hàm gọi applyphương thức. sử dụng bind()trong chức năng kết xuất sẽ tạo ra một chức năng mới mỗi lần phương thức kết xuất được gọi.
Bamieh

6

Cách sạch hơn khi xem xét một hoặc nhiều trẻ em

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

Điều này không làm việc cho tôi, nó đưa ra một lỗi: trẻ em không được xác định.
Deelux

@Deelux this.props.children thay vì trẻ em
Martin Dawson

Điều này vượt qua đứa trẻ như con của chính nó this.props. Nói chung, tôi chỉ khuyên bạn nên nhân bản với các đạo cụ cụ thể, không phải toàn bộ shebang.
Andy

Vượt qua {...this.props}không làm việc cho tôi, là cách {...child.props}chính xác?
Felipe Augusto

Đối với các thành phần chức năng:React.Children.map(children, child => React.cloneElement(child, props))
vsync

5

Không có câu trả lời nào đề cập đến vấn đề sinh con KHÔNG phải là thành phần Phản ứng, chẳng hạn như chuỗi văn bản. Một cách giải quyết có thể là một cái gì đó như thế này:

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

Bạn không còn cần {this.props.children}. Bây giờ bạn có thể quấn thành phần con bạn sử dụng rendertrong Routevà thông qua đạo cụ của bạn như bình thường:

<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
Đạo cụ kết xuất hiện là tiêu chuẩn trong React ( Reacjs.org/docs/render-props.html ) và đáng xem là câu trả lời mới được chấp nhận cho câu hỏi này.
Ian Danforth

19
Làm thế nào đây là một câu trả lời cho câu hỏi?
Maximo Toduez

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

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

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

xem ví dụ tại đây: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview



4

Nếu bạn có nhiều con bạn muốn truyền đạo cụ cho, bạn có thể thực hiện theo cách này, sử dụng React.Children.map:

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

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

Nếu thành phần của bạn chỉ có một con, không cần lập bản đồ, bạn có thể ngay lập tức cloneEuity:

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

3

Theo tài liệu của cloneElement()

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

Nhân bản và trả về một phần tử React mới bằng cách sử dụng phần tử làm điểm bắt đầu. Phần tử kết quả sẽ có các đạo cụ của phần tử gốc với các đạo cụ mới được kết hợp một cách nông cạn. Trẻ em mới sẽ thay thế trẻ em hiện có. khóa và ref từ phần tử gốc sẽ được giữ nguyên.

React.cloneElement() gần như tương đương với:

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

Tuy nhiên, nó cũng bảo tồn refs. Điều này có nghĩa là nếu bạn có một đứa trẻ với một giới thiệu về nó, bạn sẽ không vô tình đánh cắp nó từ tổ tiên của bạn. Bạn sẽ nhận được cùng một tham chiếu đến yếu tố mới của bạn.

Vì vậy, cloneEuity là những gì bạn sẽ sử dụng để cung cấp đạo cụ tùy chỉnh cho trẻ em. Tuy nhiên, có thể có nhiều con trong thành phần và bạn sẽ cần phải lặp lại nó. Những câu trả lời khác gợi ý cho bạn là ánh xạ qua chúng bằng cách sử dụng React.Children.map. Tuy nhiên, React.Children.mapkhông giống như React.cloneElementthay đổi các khóa của phần tử nối thêm và thêm .$vào làm tiền tố. Kiểm tra câu hỏi này để biết thêm chi tiết: React.cloneEuity bên trong React.Children.map đang khiến các khóa phần tử thay đổi

Nếu bạn muốn tránh nó, thay vào đó bạn nên tìm forEachchức năng như

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

}

2

Hơn nữa với câu trả lời @and_rest, đây là cách tôi nhân bản trẻ em và thêm một lớp học.

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

2

Tôi nghĩ rằng một prop prop là cách thích hợp để xử lý tình huống này

Bạn để cho Phụ huynh cung cấp các đạo cụ cần thiết được sử dụng trong thành phần con, bằng cách cấu trúc lại mã Cha mẹ để tìm kiếm một cái gì đó như thế này:

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

  return children({ doSomething })
}

Sau đó, trong Thành phần con, bạn có thể truy cập hàm được cung cấp bởi cha mẹ theo cách này:

class Child extends React {

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

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

}

Bây giờ cấu trúc fianl sẽ trông như thế này:

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

Điều gì xảy ra nếu cha mẹ chỉ là một trình bao bọc cho {con} là một văn bản trong thành phần lớp khác?
Adebayo

2

Phương pháp 1 - trẻ em nhân bản

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

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

Phương pháp 2 - sử dụng bối cảnh tổng hợp

Bối cảnh cho phép bạn chuyển một chỗ dựa cho một thành phần con sâu mà không rõ ràng chuyển nó như một chỗ dựa thông qua các thành phần ở giữa.

Bối cảnh đi kèm với nhược điểm:

  1. Dữ liệu không chảy theo cách thông thường - thông qua các đạo cụ.
  2. Sử dụng bối cảnh tạo ra một hợp đồng giữa người tiêu dùng và nhà cung cấp. Có thể khó hiểu hơn và sao chép các yêu cầu cần thiết để sử dụng lại một thành phần.

Sử dụng bối cảnh tổng hợp

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

Một chút muộn, nhưng bạn có thể giải thích các ký hiệu trong {children}:{children:ReactNode}?
camille

@camille, Đây là một bản đánh máy. Nhìn vào nó bây giờ, tôi sẽ chỉ trả lời bằng Javascript và ngay cả khi tôi sẽ viết Bản in, tôi sẽ làm khác đi. Có thể chỉnh sửa nó trong tương lai.
Ben Carp

1
@camille, về cơ bản, điều đó có nghĩa là giá trị có khóa "children"là loạiReactNode
Ben Carp

1

Cách dễ nhất để làm điều này:

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

5
Điều này không sao chép this.props.children vào this.props.children của đứa trẻ? và có hiệu lực sao chép đứa trẻ vào chính nó?
Arshabh Agarwal

1

Đối với bất kỳ ai có một yếu tố con duy nhất, điều này nên làm điều đó.

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

0

Đây có phải là những gì bạn yêu cầu?

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
Không, tôi không muốn ràng buộc nội dung của trình bao bọc của mình với một số nội dung cụ thể.
cộng-

0

Một số lý do React.children không làm việc cho tôi. Đây là những gì làm việc cho tôi.

Tôi muốn chỉ thêm một lớp học cho đứa trẻ. tương tự như thay đổi một chỗ dựa

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

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

Thủ thuật ở đây là React.cloneEuity . Bạn có thể vượt qua bất kỳ chỗ dựa nào theo cách tương tự


0

Render đạo cụ là cách tiếp cận chính xác nhất cho vấn đề này. Thay vì chuyển thành phần con cho thành phần cha làm đạo cụ con, hãy để cha mẹ kết xuất thành phần con theo cách thủ công. Render là các đạo cụ tích hợp trong phản ứng, có tham số chức năng. Trong chức năng này, bạn có thể cho phép thành phần cha mẹ kết xuất bất cứ thứ gì bạn muốn với các tham số tùy chỉnh. Về cơ bản nó làm điều tương tự như đạo cụ trẻ em nhưng nó có thể tùy chỉnh nhiều hơn.

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

Ví dụ tại codepen


0

Khi sử dụng các thành phần chức năng, bạn sẽ thường gặp TypeError: Cannot add property myNewProp, object is not extensiblelỗi khi cố gắng đặt thuộc tính mới props.children. Có một công việc xoay quanh vấn đề này bằng cách nhân bản các đạo cụ và sau đó nhân bản chính đứa trẻ bằng các đạo cụ mới.

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>
  )
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.