'React Transition Group: How to pass ref to mapped component to avoid Warning: findDOMNode is deprecated in StrictMode (still not working)
I am using "React Transition Group" ver 4.4.2 to animate menu buttons (CSSTransitions mapped inside TransitionGroup), but when I click them ones or a couple of times I get the warning:
"Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of Transition which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-find-node"
I know, I know, I have to use refs to get rid of that warning, so I did as it was described in these tickets:
But I still have the warning! I just have to click the button onece or click different menu buttons a couple of times. What is intresting is that I get that warning only once, than I have to reload page to get it again. I have inserted my part of code to the codesandbox:
Basically the code we need to look at is in NavigationItems.js and NavigationItem.js (src/components-satateLess/Navigation/NavigationItems... etc.)
As you can see I use useRef hook inside NavigationItem.js component that is mapped in NavigationItems.js:
import React, { useState } from "react";
import { TransitionGroup } from "react-transition-group";
import classes from "./NavigationItems.module.scss";
import routerButtons from "../../../router/mainMenu";
import NavigationItem from "./NavigationItem/NavigationItem"
const NavigationItems = () => {
const [allButtons, setAllButtons] = useState(routerButtons);
const [prevButton, setPrevButton] = useState({
in:false, id:-1, desc:"",href:"",element:null
});
const allButtonsDeepUpdate = (idx, obj) => {
const allButtonsCpy = [];
for(let i=0;i<allButtons.length;i++) {
if(i===idx) {
allButtonsCpy.push(Object.assign({},obj));
} else if (i===prevButton.id) {
allButtonsCpy.push(Object.assign({},prevButton));
} else {
allButtonsCpy.push(Object.assign({},allButtons[i]));
};
};
setAllButtons(allButtonsCpy);
};
const enterAnimation = (idx) => {
//this contition checks if button wasn't already animated.
if(allButtons[idx].id !== prevButton.id) {
const newButton = {...allButtons[idx], ...{in:true}};
if (prevButton.id !== -1)
setPrevButton({...prevButton,...{in:false}});
allButtonsDeepUpdate(idx, newButton);
setPrevButton(Object.assign({},allButtons[idx]));
}
};
return (
<div>
<TransitionGroup component="ul" className={classes.NavigationItems}>
{allButtons.map((button) => (
<NavigationItem
key={button.id}
starter={button.in}
timeout={1000}
click={enterAnimation.bind(this,button.id)}
link={button.href}
>
{button.desc}
</NavigationItem>
))}
</TransitionGroup>
</div>
);
};
export default NavigationItems;
Then NavigationItem.js:
import React, { useEffect, useRef } from 'react';
import { NavLink } from 'react-router-dom';
import { CSSTransition } from 'react-transition-group';
import classes from './NavigationItem.module.scss';
const NavigationItem = props => {
const ref = useRef(null);
useEffect(() => {
console.log("Ref has changed: ",ref.current);
},[ref.current])
return (
<CSSTransition
nodeRef={ref}
in={props.starter}
classNames={{
enter: classes.NavigationItemEnter,
enterActive: classes.NavigationItemEnterActive,
enterDone: classes.NavigationItemEnterDone,
exit: classes.NavigationItemExit,
exitActive: classes.NavigationItemExitActive,
exitDone: classes.NavigationItemExitDone,
}}
timeout={props.timeout}
>
<li ref={ref} className={classes.NavigationItem} onClick={props.click}>
<NavLink
//activeClassName={classes.active}
//className={({isActive}) => isActive ? classes.active : ''}
to={props.link}
exact={props.exact}
>
{props.children}
</NavLink>
</li>
</CSSTransition>
);
}
export default NavigationItem;
I've also used React.createRef() and forwardRef() with similar results. Am I missing something, or is it caused by "React Transition Group" liblary? If so, were can I report that?
As always your answers and suggestions are more than welcome!
Thanks in advance!!!
Solution 1:[1]
I had a similar problem, and I finally solved it by wrapping the inner component in the map in a div and passing the ref to the div instead of the component. I couldn't make it work by giving the ref via React.forwardRef.
Also, the naming of the elements inside TransitionGroup
is suspicious. Seems like enterDone
means enter-active
but it's not similar with exit
. Take note of what my CSS is and what the effect is.
// Inside List.js
const getItemsToDo = () => {
let checkedCopy = [...checked];
return (
<TransitionGroup className="todo-items">
{checkedCopy.reverse().map((check, index) => {
if (!check) {
let reverse_index = checked.length - 1 - index;
const textHandler = onTextChangeHandler(reverse_index);
const checkboxHandler = onCheckboxChangeHandler(reverse_index);
const itemRef = createRef();
return (
<CSSTransition
key={reverse_index}
timeout={500}
classNames={{
enterDone: styles["todo-item-enter-active"],
enterActive: styles["todo-item-enter"],
exitActive: styles["todo-item-exit-active"],
exitDone: styles["todo-item-exit"],
}}
nodeRef={itemRef}
>
<div ref={itemRef}>
<ListElement
onTextChange={textHandler}
onCheckboxChange={checkboxHandler}
checked={check}
text={texts[reverse_index]}
key={reverse_index}
/>
</div>
</CSSTransition>
);
} else return null;
})}
</TransitionGroup>
);
};
// ListElement.js
import React from "react";
const ListElement = (props) => {
const { checked, text, onCheckboxChange, onTextChange, readOnly } = props;
return (
<form onSubmit={(e) => e.preventDefault()}>
<input type="checkbox" checked={checked} onChange={onCheckboxChange} />
<input
type="text"
value={text}
onChange={onTextChange}
readOnly={readOnly}
/>
</form>
);
};
export default ListElement;
/* List.module.css */
.todo-item-enter {
opacity: 0;
}
.todo-item-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
.todo-item-exit {
opacity: 1;
}
.todo-item-exit-active {
opacity: 0;
transform: translateY(2rem);
transition: all 500ms ease-in-out;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Konrad Pagacz |