关于 React.useEffect() 和 React.useState() 的问题

Posted

技术标签:

【中文标题】关于 React.useEffect() 和 React.useState() 的问题【英文标题】:Question about React.useEffect() and React.useState() 【发布时间】:2021-01-19 21:46:37 【问题描述】:

我遇到了一个问题,即 useEffect 没有触发基于 useState 更改的重新渲染,或者 useState 没有更改而不会触发 useEffect。一旦我选择了一个应该更新 useState 作为选定组件的资产,然后我选择了另一个它没问题,但是一旦我选择了一个已经被选择的资产,我就注意到了这个问题。没有任何反应?任何建议或任何东西都非常感谢!谢谢!

export default function SingleAsset(svg, name, size = '60', group) 
  const [assetType, setAssetType] = React.useState(null);
  const [officeType, setOfficeType] = React.useState(null);
  const [industrialType, setIndustrialType] = React.useState(null);
  const [financingType, setFinancingType] = React.useState(null);
  const [investmentType, setInvestmentType] = React.useState(null)
  const acquistionStatus = useSelector(state => state.Acquisition)
  const dispatch = useDispatch()
  const classes = useStyles()

  React.useEffect(() => 
    if(financingType === 'Acquisition') 
      const data = financingType
      dispatch(updateFormData(data))
      dispatch(toggleAcquisitionOn())
    
    if(financingType) 
      if(financingType !== 'Acquisition')  dispatch(toggleAcquisitionOff())
      const data = financingType
      dispatch(updateFormData(data))
      
    
    if(industrialType) 
      const data = industrialType
      dispatch(updateFormData(data))
    
    if(officeType) 
      const data = officeType
      dispatch(updateFormData(data))
    
    if(investmentType) 
      const data = investmentType
      dispatch(updateFormData(data))
      console.log(data)
    
    if(assetType) dispatch(updateAssetData(assetType))
    console.log(financingType)
    console.log(officeType)
    console.log(industrialType)
    console.log(investmentType)
    
  ,[investmentType,assetType,officeType,industrialType,financingType])

  const handleSelect = (group, name) => 
    switch(group) 
      case 'main':
        setAssetType(name)
        break
      case 'office': 
        setOfficeType(name)
        break
      case 'industrial':
        setIndustrialType(name)
        break
      case 'financing':
        setFinancingType(name)
        break
      case 'investment':
        setInvestmentType(name)
        break
      default:
        throw new Error('group not found')
    
  

  return (
    <Grid
      className=classes.container 
      item
    >
      <Grid
        container
        direction="column"
        alignItems="center"
      >
        <IconButton onClick=() => handleSelect(group, name)>
          <img src=svg color="white" height=size />
        </IconButton>
        <Typography 
          variant="body1" 
          color="white" 
          align="center"
        >
          name
        </Typography>
      </Grid>
    </Grid>         
  )

【问题讨论】:

那是因为由于状态值没有变化,所以不会触发使用效果,也不会重新渲染。 完全。我只是不确定为什么在重新选择资产时它不会更新。当资产被多次点击时,您是否能够使用相同的数据或以前的数据更新状态? 【参考方案1】:

这实际上是一种预期的行为。

React 使用“浅比较”(查看this other great question 了解更多信息),这实质上意味着它将与=== 比较以前的值和新值。这就是不应该改变状态对象的原因。因此,当您的代码尝试将状态更新为它已经拥有的相同值时,它实际上不会这样做......它是相同的,因此不会触发重新渲染。

为了解决这个问题,我们可以强制 React 使用 some clever coding 进行更新,这将使 React 检测到状态变化:

// Setup a new state 
const [, updateState] = useState();
// Create a function that will update state with a new object
// This works because  ===  is always false, making React trigger a re-render
const forceUpdate = useCallback(() => updateState(), []);

您的示例代码将是这样的:

export default function SingleAsset(svg, name, size = '60', group) 
  const [, updateState] = useState();
  const forceUpdate = useCallback(() => updateState(), []);
  const [assetType, setAssetType] = React.useState(null);
  const [officeType, setOfficeType] = React.useState(null);
  const [industrialType, setIndustrialType] = React.useState(null);
  const [financingType, setFinancingType] = React.useState(null);
  const [investmentType, setInvestmentType] = React.useState(null)
  const acquistionStatus = useSelector(state => state.Acquisition)
  const dispatch = useDispatch()
  const classes = useStyles()

  React.useEffect(() => 
    if(financingType === 'Acquisition') 
      const data = financingType
      dispatch(updateFormData(data))
      dispatch(toggleAcquisitionOn())
    
    if(financingType) 
      if(financingType !== 'Acquisition')  dispatch(toggleAcquisitionOff())
      const data = financingType
      dispatch(updateFormData(data))
      
    
    if(industrialType) 
      const data = industrialType
      dispatch(updateFormData(data))
    
    if(officeType) 
      const data = officeType
      dispatch(updateFormData(data))
    
    if(investmentType) 
      const data = investmentType
      dispatch(updateFormData(data))
      console.log(data)
    
    if(assetType) dispatch(updateAssetData(assetType))
    console.log(financingType)
    console.log(officeType)
    console.log(industrialType)
    console.log(investmentType)
    
  ,[investmentType,assetType,officeType,industrialType,financingType])

  const handleSelect = (group, name) => 
    switch(group) 
      case 'main':
        setAssetType(name)
        break
      case 'office': 
        setOfficeType(name)
        break
      case 'industrial':
        setIndustrialType(name)
        break
      case 'financing':
        setFinancingType(name)
        break
      case 'investment':
        setInvestmentType(name)
        break
      default:
        throw new Error('group not found')
    
  

  return (
    <Grid
      className=classes.container 
      item
    >
      <Grid
        container
        direction="column"
        alignItems="center"
      >
        <IconButton onClick=() => 
          handleSelect(group, name);
          forceUpdate(); // <-- this is were the magic happens
        >
          <img src=svg color="white" height=size />
        </IconButton>
        <Typography 
          variant="body1" 
          color="white" 
          align="center"
        >
          name
        </Typography>
      </Grid>
    </Grid>         
  )

请注意强制重新渲染应该是最后的手段,重新渲染可能是一项相当昂贵的操作,应该小心处理。

【讨论】:

以上是关于关于 React.useEffect() 和 React.useState() 的问题的主要内容,如果未能解决你的问题,请参考以下文章

如果添加了功能依赖关系,则 React useEffect re Renders Infinitely

TypeError: (0 , _react.useEffect) 不是函数

[react] useEffect和useLayoutEffect有什么区别?

React UseEffect 不适用于 firepad 和 firebase

无法访问 React useEffect 中数组的方法和属性

React useEffect 如何监视和更新状态? [复制]