I'm using the package react-transition-group, I have tried using the nodeRef props on the CSSTransition component, and added a wrapper on my component but I still get the warning regarding findDOMNode.
Here's the code:
<CSSTransition
key={entry.id}
timeout={500}
classNames="timesheet-entry"
>
<TimesheetEntry
taskOptions={taskOptions || []}
deleteHandler={(event) => {
deleteHandler(event, entry.id.toString());
}}
data={entry}
dateChangeHandler={(date: Date) =>
dateChangeHandler(date, entry.id)
}
hoursChangeHandler={(event) => hoursChangeHandler(event, entry.id)}
taskCodeChangeHandler={(event, value) =>
taskCodeChangeHandler(event, value, entry.id)
}
/>
</CSSTransition>
Code for the TimesheetEntry component:
function TimesheetEntry(props: TimesheetEntryProps) {
return (
<div>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
label="Date"
style={{ marginRight: '15px', height: '20px', marginTop: '-2px' }}
disableToolbar
variant="inline"
format="MM/dd/yyyy"
margin="normal"
value={props.data.date}
onChange={props.dateChangeHandler}
size="small"
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</MuiPickersUtilsProvider>
<Autocomplete
size="small"
style={{
width: 300,
display: 'inline-block',
marginRight: '15px',
}}
options={props.taskOptions}
getOptionLabel={(option) => option.name}
getOptionSelected={(option, value) => {
return option.id === value.id && option.name === value.name;
}}
onChange={props.taskCodeChangeHandler}
renderInput={(params) => (
<TextField {...params} label="Task" variant="outlined" />
)}
/>
<TextField
size="small"
style={{ marginRight: '15px', height: '20px' }}
label="Hours"
type="number"
inputProps={{ min: 0.5, step: 0.5 }}
onChange={props.hoursChangeHandler}
InputLabelProps={{
shrink: true,
}}
/>
<Button
style={{ marginRight: '15px' }}
variant="contained"
color="secondary"
size="small"
startIcon={<DeleteIcon />}
onClick={props.deleteHandler}
>
Delete
</Button>
</div>
);
}
export default TimesheetEntry;
I've also made a somewhat similar code setup in codesandbox here
I've tried adding nodeRef and a ref reference through a div wrapper on my TimesheetEntry component but that seems to make the animation behave improperly(adding new entries works properly but when I try to delete the entry, the animation doesn't seem to work anymore). I'm also looking for a way without creating a div wrapper on the TimesheetEntry component.
There are actually two distinct findDOMNode
warnings in your CodeSandbox demo:
When you first add or remove an entry, which originates from the direct usage of react-transition-group
for TimesheetEntry
.
When you save your timesheet, which originates from the indirect usage of react-transition-group
through Material UI's Snackbar
component.
Unfortunately, you have no control over the latter, so let's fix the former; you're managing a list of transitioning TimesheetEntry
components, but to correctly implement nodeRef
, each element needs a distinct ref object, and because you cannot call React hooks within a loop (see rules of hooks), you have to create a separate component:
const EntryContainer = ({ children, ...props }) => {
const nodeRef = React.useRef(null);
return (
<CSSTransition
nodeRef={nodeRef}
timeout={500}
classNames="timesheet-entry"
{...props}
>
<div ref={nodeRef}>
{children}
</div>
</CSSTransition>
);
};
which you will wrap around TimesheetEntry
:
const controls: JSX.Element[] = entries.map((entry: entry, index: number) => {
return (
<EntryContainer key={entry.id}>
<TimesheetEntry
deleteHandler={event => {
deleteHandler(event, entry.id.toString());
}}
data={entry}
dateChangeHandler={(date: Date) => dateChangeHandler(date, entry.id)}
hoursChangeHandler={event => hoursChangeHandler(event, entry.id)}
taskCodeChangeHandler={(event, value) =>
taskCodeChangeHandler(event, value, entry.id)
}
/>
</EntryContainer>
);
});
You said that you tried something like this already, but my suspicions are that you forgot to forward EntryContainer
's props to CSSTransition
, which is the crucial step because those are being passed down by TransitionGroup
.
Caleb Taylor's answer solves the problem by forwarding the ref, and you can avoid using // @ts-ignore
if you test the child element's validity before cloning it. For instance,
import { Children, cloneElement, isValidElement, useRef } from 'react';
import { CSSTransition as _CSSTransition } from 'react-transition-group';
import { CSSTransitionProps } from 'react-transition-group/CSSTransition';
export const CSSTransition = ({ children, ...props }: CSSTransitionProps) => {
const nodeRef = useRef(null);
return (
<_CSSTransition {...props} nodeRef={nodeRef}>
<>
{Children.map(children, child =>
isValidElement(child)
? cloneElement(child, { ref: nodeRef })
: child
)}
</>
</_CSSTransition>
);
};
The idea came from this answer to an unrelated question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With