Kiedy powinienem używać React.cloneElement, a kiedy this.props.children?

118

Nadal jestem noobem w React i w wielu przykładach w Internecie widzę tę różnicę w renderowaniu elementów potomnych, co jest dla mnie mylące. Zwykle widzę to:

class Users extends React.Component {
  render() {
    return (
      <div>
        <h2>Users</h2>
        {this.props.children}
      </div>
    )
  }
}

Ale potem widzę taki przykład:

<ReactCSSTransitionGroup
     component="div"
     transitionName="example"
     transitionEnterTimeout={500}
     transitionLeaveTimeout={500}
     >
     {React.cloneElement(this.props.children, {
       key: this.props.location.pathname
      })}
</ReactCSSTransitionGroup>

Teraz rozumiem api, ale doktorzy nie wyjaśniają dokładnie, kiedy powinienem go używać.

Więc co robi jedno, czego drugie nie może? Czy ktoś mógłby mi to wyjaśnić lepszymi przykładami?

Amit Erandole
źródło
3
youtube.com/watch?v=hEGg-3pIHlE ten facet pokazuje, jak używa cloneElement. Sprawdź to, aby zobaczyć kilka przykładów
iurii

Odpowiedzi:

69

Edytować:

Spójrz na odpowiedź Vennesa za Zamiast tego, które jest lepsze wyjaśnienie.

Oryginalny:

Po pierwsze, React.cloneElementprzykład działa tylko wtedy, gdy Twoje dziecko jest pojedynczym elementem React.

Bo prawie wszystko {this.props.children}jest tym, czego chcesz. Klonowanie jest przydatne w niektórych bardziej zaawansowanych scenariuszach, w których rodzic wysyła element, a komponent potomny musi zmienić pewne właściwości tego elementu lub dodać takie rzeczy, jak ref, aby uzyskać dostęp do rzeczywistego elementu DOM.

W powyższym przykładzie rodzic, który przekazuje dziecku, nie wie o wymaganiu klucza dla komponentu, dlatego tworzy kopię podanego elementu i dodaje klucz na podstawie jakiegoś unikalnego identyfikatora w obiekcie. Aby uzyskać więcej informacji na temat tego, co robi klucz: https://facebook.github.io/react/docs/multiple-components.html

Morten Olsen
źródło
183

props.childrennie są rzeczywistymi dziećmi; To jest descriptorsprawa dzieci. Więc właściwie nie masz nic do zmiany; nie możesz zmieniać żadnych właściwości ani edytować żadnych funkcji; możesz tylko read from it. Jeśli chcesz wprowadzić jakieś modyfikacje, musisz je utworzyć new elementsza pomocą React.CloneElement.

https://egghead.io/lessons/react-use-react-cloneelement-to-extend-functionality-of-children-components

Przykład:

główna funkcja renderowania komponentu, taka jak App.js:

render() {   
    return(
            <Paragraph>
              <Sentence>First</Sentence>
              <Sentence>Second</Sentence>
              <Sentence>Third</Sentence>
            </Paragraph>   
    ) 
}

teraz powiedzmy, że musisz dodać onClickdo każdego dziecka z Paragraph; więc w swoim Paragraph.jsmożesz zrobić:

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

możesz po prostu zrobić to:

render() {   
  return(
        <Paragraph onClick={this.onClick}>
          <Sentence>First</Sentence>
          <Sentence>Second</Sentence>
          <Sentence>Third</Sentence>
        </Paragraph>   
   ) 
}

Uwaga:React.Children.map funkcja będzie zobaczyć tylko top levelelementy, nie widzi żadnej z rzeczy, że te elementy czynią; co oznacza, że ​​udostępniasz bezpośrednie rekwizyty dzieciom (tutaj <Sentence />elementy). Jeśli chcesz, aby rekwizyty były przekazywane dalej, powiedzmy, że masz <div></div>wewnątrz jeden z <Sentence />elementów, który chce użyć onClickrekwizytu, wtedy w takim przypadku możesz użyć, Context APIaby to zrobić. Niech Paragraphdostawca i Sentenceelementy będą konsumentami.

Vennesa
źródło
11
To jasna odpowiedź na przykładzie prawdziwego świata. Potrzebuje więcej głosów poparcia.
SeaWarrior404
1
Zgadzam się z @ SeaWarrior404 - wydaje mi się, że OP szukał iteracji nad kolekcją, a nie pojedynczym dzieckiem, ponieważ jego interfejs użytkownika ma w tytule „Users” / liczba mnoga. Używanie React.Children.mapw połączeniu z React.cloneElementas @vennesa wskazuje, jest bardzo potężne
Jakeforaker
23

W rzeczywistości React.cloneElementnie jest ściśle powiązany z this.props.children.

Jest to przydatne, gdy trzeba sklonować elementy reagujące ( PropTypes.element), aby dodać / nadpisać właściwości, bez potrzeby, aby rodzic miał wiedzę o tych elementach wewnętrznych (np. Dołączanie programów obsługi zdarzeń lub przypisywanie key/ refatrybuty).

Również elementy reakcji są niezmienne .

React.cloneElement (element, [props], [... children]) jest prawie równoważne z: <element.type {...element.props} {...props}>{children}</element.type>


Jednak childrenprop w React jest szczególnie używany do zawierania (aka kompozycji ), parowania z React.ChildrenAPI i React.cloneElementkomponentu, który używa właściwości. Dzieci mogą obsługiwać więcej logiki (np. Przejścia stanów, zdarzenia, pomiary DOM itp.) Wewnętrznie, podczas oddawania części renderującej do Gdziekolwiek jest używany, React Router <switch/>lub komponent złożony <select/>są świetnymi przykładami.

Ostatnią rzeczą, o której warto wspomnieć, jest to, że elementy reagujące nie są ograniczone do rekwizytów dzieci.

function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}

Mogą być cokolwiek rekwizyty, które ma sens, kluczowe było określenie umowę dobre dla komponentu tak, że konsumenci z niej może być oddzielona od szczegółów implementacji bazowych, niezależnie czy to za pomocą React.Children, React.cloneElementlub nawet React.createContext.

Xlee
źródło