使用 React,在使用 react-transition-group 时,在 StrictMode 中不推荐使用 findDOMNode 作为警告

Posted

技术标签:

【中文标题】使用 React,在使用 react-transition-group 时,在 StrictMode 中不推荐使用 findDOMNode 作为警告【英文标题】:Using React, findDOMNode is deprecated in StrictMode is thrown as a warning when using react-transition-group 【发布时间】:2020-09-23 00:20:59 【问题描述】:

我正在使用包 react-transition-group,我尝试在 CSSTransition 组件上使用 nodeRef 道具,并在我的组件上添加了一个包装器,但我仍然收到有关 findDOMNode 的警告。

代码如下:

 <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>

TimesheetEntry 组件的代码:

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;

我也在代码沙箱here中做了一个类似的代码设置

我尝试通过我的 TimesheetEntry 组件上的 div 包装器添加 nodeRef 和 ref 引用,但这似乎使动画行为不正确(添加新条目可以正常工作,但是当我尝试删除条目时,动画不会似乎不再工作了)。我也在寻找一种无需在 TimesheetEntry 组件上创建 div 包装器的方法。

【问题讨论】:

您的时间表组件返回多个元素,所以我不确定您是否能够在不使用 div 包装的情况下实现这一目标。在此更改日志中,它提到您应该使用 noderef github.com/reactjs/react-transition-group/blob/… @erich 我已经尝试过,在实际询问之前,正如我在帖子中提到的那样,问题是当我尝试它时,警告不再显示,但删除 TimesheetEntry 时的动画没有更长的工作正常,也会发生最后一个项目总是被删除。 【参考方案1】:

在您的 CodeSandbox 演示中实际上有两个不同的 findDOMNode 警告:

    当您第一次添加或删除一个条目时,它源于将react-transition-group 直接用于TimesheetEntry

    当您保存时间表时,这源于通过 Material UI 的 Snackbar 组件间接使用 react-transition-group

不幸的是,你无法控制后者,所以让我们修复前者;您正在管理转换 TimesheetEntry 组件的列表,但要正确实现 nodeRef,每个元素都需要一个不同的 ref 对象,并且因为您不能在循环中调用 React 钩子(请参阅 rules of hooks),您必须创建一个单独的组件:

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>
  );
;

你将围绕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>
  );
);

您说您已经尝试过这样的事情,但我怀疑您忘记将EntryContainer 的道具转发给CSSTransition,这是至关重要的一步,因为这些是由TransitionGroup 传递的。

【讨论】:

这就是我卡住的地方,因为我不能在循环中使用 hooks(useRef),我只是想不出创建一个 EntryContainer 来规避它。谢谢! 我觉得我在这里遇到了类似的问题......但我检查了我正在传递我的道具,一切都很好。退出转换工作,但不进入。有人可以发现这个包装器组件中的错误吗? (TS)gist.github.com/stevecastaneda/899e83d47fa27397c447e2e6ae3d7771 我不知道这是否是一个类似的问题,但我建议发布一个新问题,解释这个包装器的目的是什么以及为什么你使用 Transition 而不是 CSSTransition,它包含一个关键的回流CSS 转换按预期工作所需的 hack,as explained in the docs。 @silvenon 我有一个不相关的问题。如果我只想有 children 而没有包装 ,那么解决方案是什么。我陷入了困境,因为如果没有包装 的引用,它就无法工作,我想摆脱它,因为它会影响我的布局很多 @IceWhisper nodeRef has 指的是包装过渡内容的 DOM 节点。我需要更多地了解您的情况,以便为您的具体情况找到解决方案。也许最好再问一个 Stack Overflow 问题,然后在 Twitter 上联系我。 (@silvenon) 【参考方案2】:

我的答案使用与@sivenon 的答案相同的策略,但是在使用转换的组件中编写占用空间的“无用”引用很烦人。所以这里有一个包装器,它可以为你添加参考而不使用隐藏的 div,因为额外的标记会破坏样式。

import React,  useRef  from 'react';
import  CSSTransition as _CSSTransition  from 'react-transition-group';
import  CSSTransitionProps  from 'react-transition-group/CSSTransition';

const CSSTransition = (props: CSSTransitionProps) => 
  const nodeRef = useRef(null);

  return (
    <_CSSTransition ...props nodeRef=nodeRef>
      <>
        React.Children.map(props.children, (child) => 
          // @ts-ignore
          return React.cloneElement(child,  ref: nodeRef );
        )
      </>
    </_CSSTransition>
  );
;

export default CSSTransition;

【讨论】:

以上是关于使用 React,在使用 react-transition-group 时,在 StrictMode 中不推荐使用 findDOMNode 作为警告的主要内容,如果未能解决你的问题,请参考以下文章

react系列在React中使用Redux

使用通过 react、redux 和 react-redux 完成的组件以及在 react 应用程序中使用 webpack 构建时出错

使用 React,在使用 react-transition-group 时,在 StrictMode 中不推荐使用 findDOMNode 作为警告

[react] 在React怎么使用Context?

无法在 React Native 应用程序中使用 React.memo

是否可以在 React 中使用 Polymer?