Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refs preserved with React.cloneElement()

I don't get the point of statements written in React official docs:

cloneElement()

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

Clone and return a new React element using element as the starting point. The resulting element will have the original element’s props with the new props merged in shallowly. New children will replace existing children. key and ref from the original element will be preserved.

React.cloneElement() is almost equivalent to:

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

However, it also preserves refs. This means that if you get a child with a ref on it, you won’t accidentally steal it from your ancestor. You will get the same ref attached to your new element.

What makes me confused me is the statement This means that if you get a child with a ref on it, you won’t accidentally steal it from your ancestor. You will get the same ref attached to your new element.

If I am understanding Okay, the ref that points to Child element in Parent component will be preserved even if the Parent gets cloned. So after React.cloneElement(Parent), there are two individual Parents(which have same deep values inside, including ref), and both have refs respectively, and those refs points to same single Child. Am I corrent?

Then what works with ancestor? What is the ancestor in this context?

like image 857
cadenzah Avatar asked Feb 09 '19 13:02

cadenzah


1 Answers

To give you can example on what the docs seem to Illustrate, lets consider a component App that renders a component Main and which has two children Home and Dashboard for which it specifies refs

class App extends React.Component {
   constructor(props) {
      super(props);
      this.homeRef = React.createRef();
      this.dashboardRef = React.createRef();
   }
   render() {
      return (
          <Main>
               <Home ref={this.homeRef} key={'Home'}/>
               <Dashboard ref={this.dashboardRef} key={'Dashboard'}/>
          </Main>
      )         
   }
}

Now the main component clones the child elements to add a props onClick to its children,

class Main extends React.Component {
   onClick = () => {}
   render() {
       return (
          <div>
              {/* Some content here */}
              {React.Children.map(this.props.children, child => {
                 return React.cloneElement(child, {onClick: this.onClick})
               })}
          </div>
       )
   }
}

Now when the Main component clones the children in its like

React.cloneElement(child, {onClick: this.onClick})

which in our case are the Home and Dashboard component, if cloneElement were to ignore the key and ref passed to them by the App component, the App component won't have access to these children that it rendered. Hence React.cloneElement preserves the refs and keys passed to the elements even if they are cloned in child render function.

The component created by createdElement is cloned child, and what is used by <Main> is the cloned child, then what does ref in <App> point? Original child, or cloned child?

Does the phrase accidentally steal means something like forbidden access

Ref to an element only makes sense when the element is rendered in the DOM. In the above case the original element is not rendered but the cloned element is. Had React not assigned the same ref to the cloned component which is rendered in the DOM, App component wouldn't be interacting with anything significant. Also if at all you decide to render both original children and the cloned children, the ref will point to the cloned children.

Demo illustrating the above

P.S. CloneElement is not used to clone components, rather than rendered JSX instances and is done mostly to add more props to the

children component rendered from elsewhere

I hope the above example explains the scenario

like image 61
Shubham Khatri Avatar answered Oct 08 '22 10:10

Shubham Khatri