【问题标题】:Having trouble returning function in custom React hook在自定义 React 钩子中返回函数时遇到问题
【发布时间】:2020-01-23 14:14:35
【问题描述】:

我目前正在尝试创建一个 useRadioList() 挂钩,以跟踪给定组件中所有子级的 isExpanded 道具(当一个子级切换 isExpanded 时,其他子级取消切换)

我这样做的方式是:

  1. 通过创建与子项长度相同的假值列表来设置初始状态
  2. 将此状态映射到每个孩子的 isExpanded 道具
  3. 声明并返回一个操纵初始列表状态的切换函数
// useRadioList.jsx
import React, { Children, isValidElement, cloneElement, useState, useEffect } from "react"

export default (children, allowMultiple = true) => {


// 1) Setup initial list 

  const [radioList, setRadioList] = useState(Array(children.length).fill(false))

// 2) Map radioList values to component children on initial render, and do so again if radioList changes

  useEffect(() => {

    Children.map(children, (child, idx) => {
      if (!isValidElement(child)) {return}

      return cloneElement(child, {isExpanded: radioList[idx]})
    })
  }, [radioList])



// 3) Declare "toggle()" to modify the list that keeps track of what indexes are active

  const toggle = (targetIndex) => {

    let newList = radioList.map((item, idx) => {
      if (allowMultiple) {
        return targetIndex == idx ? !item : item
      } else {
        return targetIndex == idx ? !item : false
      }
    })
    setRadioList(newList)
  }

  return toggle
}

// expected:  const toggle = useRadioList(children)

当我调用切换时,我收到以下错误:

警告:来自 useState() 和 useReducer() Hooks 的状态更新 不支持第二个回调参数。执行副作用 渲染完成后,在组件体中使用 useEffect() 进行声明。

编辑:

setRadioList(...newList) ----> setRadioList(newList)

不再收到callback 错误,现在我似乎无法有效地将状态映射到子级,因为isExpanded 道具在初始渲染后没有出现在每个子级中。

【问题讨论】:

    标签: javascript reactjs jsx react-hooks


    【解决方案1】:

    操作孩子的代码,不会改变原来的孩子,而是创建一个新的,所以下面的代码没有效果:

       Children.map(children, (child, idx) => {
          if (!isValidElement(child)) {return}
    
          return cloneElement(child, {isExpanded: radioList[idx]})
        });
    

    尝试返回新的孩子,像这样:

    export default (originalChildren, allowMultiple = true) => {
    
    
      // 1) Setup initial list 
    
        const [radioList, setRadioList] = useState(Array(originalChildren.length).fill(false))
        const [children, setChildren] = useState(originalChildren);
    
      // 2) Map radioList values to component children on initial render, and do so again if radioList changes
    
        useEffect(() => {
          setChildren(children => Children.map(children, (child, idx) => {
            if (!isValidElement(child)) {return}
    
            return cloneElement(child, {isExpanded: radioList[idx]})
          }));
    
        }, [radioList])
    
    
    
      // 3) Declare "toggle()" to modify the list that keeps track of what indexes are active
    
        const toggle = useCallback((targetIndex) => {
          setRadioList(radioList => radioList.map((item, idx) => {
            if (allowMultiple) {
              return targetIndex == idx ? !item : item
            } else {
              return targetIndex == idx ? !item : false
            }
          }))
        }, [allowMultiple])
    
        return { toggle, children }
      }
    
      // use like this:  const { toggle, children } = useRadioList(children)
    

    【讨论】:

      【解决方案2】:

      setRadioList 必须像这样使用:

      setRadioList(newList)
      

      扩展运算符拆分数组并将每个值作为单独的参数发送,例如setRadioList(...[1,2,3]) 会变成 setRadioList(1, 2, 3) 这当然是不正确的。

      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_array_literals

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-20
        相关资源
        最近更新 更多