-
Notifications
You must be signed in to change notification settings - Fork 47.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Get props.children
DOM handle: React.findDOMNode(child)
#4244
Comments
This behavior is correct. If you want to find one of your children's DOM nodes, you have to add a ref first (using React.cloneElement) and then use that ref. (An element is little more than the |
(The next commend in the thread you linked contradicts that. We do guarantee that children have their componentDidMount/DidUpdate called before the parent gets it.) |
@spicyj Thank you for the info and correction. Here is a working demo with // `render`
React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {
ref: `item-${child.props.key}`
});
});
// ...
// `componentDidMount` or `componentDidUpdate`
React.Children.forEach(this.props.children, (child) => {
console.log(React.findDOMNode(this.refs[`item-${child.props.key}`]));
}); |
@spicyj how would you handle a situation where some child component should find a dom node down stream? For instance:
|
I'm wondering the same thing as @mikkoh, any solution? |
@spicyj It may not be possible or feasible to add refs to children nodes, in which case how does one query a DOM node downstream from a |
@miguel-guedes @VinSpee @miguel-guedes: Look at the solution @MadLittleMods provided, his solution is correct.
Immutable props does not prevent cloning.
If you use callback refs, it's always feasible, because you can have the new callback ref also call the previous callback ref, making it completely transparent. Or you can have whomever does have the ref pass down a reference to the actual dom node. Or you can call Having said all that... Usage questions are better answered on sites like StackOverflow, as we try to use github issues for tracking bugs in the React core. We generally do not answer usage questions on github. |
@MadLittleMods if some
|
Unfortunately it's not possible to add a ref to a functional component, so how can we find the DOM node of a children that is a functional component? |
I have two buttons inside Tooltip Component, and I don't want to give a wrapper for btns and also could get the DOMs from children. |
It's because you can pass ref to built-in DOM elements but you can't pass ref to your components directly. |
Hey @iamyoki, Doing what you did works when I have one Wrapper: <WrapperOne>
{props.children}
</WrapperOne> But in my case I need to nest wrap props.children: <WrapperOne>
<WrapperTwo>
{props.children}
</WrapperTwo>
</WrapperOne> Where each Wrapper are functional components that add some logic (bind events, set classes etc.). In that case, I can get the ref in WrapperTwo with |
@trompx Of course, if you want to get |
Thanks for the prompt answer @iamyoki. const ref = useRef();
const clone = React.Children.map(children, (child) => {
// By looping once more, I manage to get the initial props.children that I would get if I
// didn't have WrapperTwo. But I get undefined in useEffect.
React.Children.map(child.props.children, (nestedchild) => {
return React.cloneElement(
nestedchild,
{
ref: (dom) => (ref.current = dom),
}
);
});
});
useEffect(() => {
console.log(ref);
}, []); In the meantime, I found another way. const oneRef = useWrapperOne();
const twoRef = useWrapperTwo();
const mergedRef = useComposeRefs(oneRef, twoRef);
return (
<>
{React.Children.map(children, (child) =>
React.cloneElement(child, {
ref: mergedRef
})
)}
</>
); But I am still wondering how to do it the way I mentioned before. |
@trompx Sure. function traverseMap(nextChildren, callback) {
return React.Children.map(nextChildren, child=>({
...callback && callback(child),
children: child.props.children && traverseMap(child.props.children, callback)
}))
} Find your target comp by name and what u want traverseMap(children, child=>{
if(child.type.name === 'WrapperTwo' || child.displayName === 'WrapperTwo') {
return {
...child,
props: {
...child.props,
ref: dom => {if(dom) ref.current = dom}
}
}
}
return child
}) Complete code function traverseMap(nextChildren, callback) {
return React.Children.map(nextChildren, child=>({
...callback && callback(child),
children: child.props.children && traverseMap(child.props.children, callback)
}))
}
traverseMap(children, child=>{
if(child.type.name === 'WrapperTwo' || child.displayName === 'WrapperTwo') {
return {
...child,
props: {
...child.props,
ref: dom => {if(dom) ref.current = dom}
}
}
}
return child
})
|
I strongly recommend against doing invasive transformations like this. This kind of code is brittle and difficult to use and understand. Wrap the children in a |
I strongly agree with Dan :) |
Thanks for the input @gaearon, but I often have to wrap components with 2-5 of those wrapper components and I'd rather avoid to add the same amount of extra |
What do these wrappers do? |
The goal was to add interactivity to some elements, make them react to different events/actions: hover/click/load/onscreen/scroll etc. <HoverWrapper
animateIn={rollhoverIn}
animateOut={rollhoverOut}>
<Component>
</HoverWrapper> Some elements need only one wrapper but some more. I wanted to have each in its own either component or hook for readability.
But maybe there is a better approach? |
@trompx I think you should redesign the use case. Like this. <Animation>
{({step, rollhoverInProps, rollhoverOutProps}) => (
<div {...(step === 'in' ? rollhoverInProps : rollhoverOutProps)}>...</div>
)}
</Animation> Also, you can try a library transition-hook that I wrote. const [onOff, setOnOff] = useState(true)
const {stage, shouldMount} = useTransition(onOff, 300) // (state, timeout)
return <div>
<button onClick={()=>setOnOff(!onOff)}>toggle</button>
{shouldMount && (
<p style={{
transition: '.3s',
opacity: stage === 'enter' ? 1 : 0
}}>
Hey guys, I'm fading
</p>
)}
</div> |
When trying to use
React.findDOMNode
on aprops.children
item(ReactElement
), an error is thrown.Here is a small demo that shows off the problem.
The parent component is mounted
console.log('isMounted', this.isMounted());
->isMounted true
.Related to #1602 and #1373.
The text was updated successfully, but these errors were encountered: