【问题标题】:useEffect hook called on initial render without dependency changing在初始渲染时调用 useEffect 钩子而不改变依赖关系
【发布时间】:2020-12-27 08:14:48
【问题描述】:

好的,我遇到了一些我不太理解的行为。 我有这个 useState 钩子

const [permanent, setPermanent] = useState(false)

还有这个 useEffect 钩子

    useEffect(() => {
        if (permanent) {
            dispatch({ value: 'Permanent booth', key: 'period' })
        } else {
            dispatch({ value: '0', key: 'period' })
        }
    }, [permanent])

它会在初始渲染时触发重新渲染,并且在渲染我的组件时我不会调用setPermanent,我已经通过评论我的应用程序中的每个setPermanent 调用来检查这一点。而且我还尝试将其替换为记录到控制台的函数。

    //const [permanent, setPermanent] = useState(false)
    const permanent = false
    const setPermanent = () => {
        console.log('I am called') //does not get called on initial render
    }

我知道它会触发重新渲染,因为当我在其中注释第二个调度调用之一时,它不会触发重新渲染。

    useEffect(() => {
        if (permanent) {
            dispatch({ value: 'Permanent booth', key: 'period' })
        } else {
            //dispatch({ value: '0', key: 'period' })
        }
    }, [permanent])

这是有原因的吗,因为我似乎找不到解释这种行为的文档?

编辑 --------------

    const shopOptions = (() => {
        const options = [
            { label: 'Choose a shop', value: '0' },
        ]
        Object.keys(stores).forEach(store => {
            options[options.length] = { label: store, value: options.length }
        })
        return options
    })()

    const genderOptions = [
        { label: 'Choose a gender', value: '0' },
        { label: 'Female', value: '1' },
        { label: 'Male', value: '2' }
    ]

    const periodOptions = [
        { label: 'Choose a period', value: '0' },
        { label: '1 week', value: '1' },
        { label: '2 weeks', value: '2' },
        { label: '3 weeks', value: '3' },
        { label: '4 weeks', value: '4' }
    ]

    const initialState = {
        shop: shopOptions[0],
        gender: genderOptions[0],
        period: periodOptions[0],
    }

    function reducer(prevState, { value, key }) {
        const updatedElement = { ...prevState[key] }
        updatedElement.value = value
        return { ...prevState, [key]: updatedElement }
    }

    //form
    const [state, dispatch] = useReducer(reducer, initialState)

【问题讨论】:

  • 调度是否做了会导致组件卸载和重新安装的事情? (例如,父母进入加载状态)。您可以返回一个清理函数,该函数仅将某些内容记录到控制台并在其中设置一个断点以查看当时发生了什么。 (由于组件和 React 的渲染过程之间存在抽象层和解耦,这并不像听起来那么简单。)

标签: javascript reactjs react-hooks state


【解决方案1】:

useEffect 钩子在第一次渲染后和每次更新传递给依赖数组的变量后运行(在您的情况下为 [permanent])。

因为你有一个触发效果的布尔值,所以很难知道它是第一次渲染还是在效果内重新渲染。在您的情况下,我会考虑不在这里使用useEffect,而是在更新状态时分派您需要的内容。例如:

const [permanent, setPermanent] = useState(false)

const makePermanent = () => {
  setPermanent(true)
  dispatch({ value: 'Permanent booth', key: 'period' })
}

const makeTemporary = () => {
  setPermanent(false)
  dispatch({ value: '0', key: 'period' })
}

【讨论】:

  • 我不知道所有 useEffect 挂钩在初始渲染时运行,有没有办法防止这种情况?我刚刚意识到我的整个应用程序到处都在运行 useEffects,这实际上解释了很多。 componentDidUpdate 没有真正的替代品吗?
  • @JonasGrønbek @JonasGrønbek article 提供了一种模仿 componentDidUpdate 的方法,但我强烈建议不要经常这样做。这(非常全面)article我反对尝试复制类生命周期方法。祝你好运!
【解决方案2】:

这是我的解决方案。只需一个布尔属性和排序钩子就可以完成这项工作。

const _isMounted = React.useRef(false)
const [filter, setFilter] = React.useState('')

React.useEffect(() => {
  if (_isMounted.current) {
    getData(filter) // Now it will not get called very first time
  }
}, [filter])

/* Order is important. [] or so called componentDidMount() will be the last */
React.useEffect(() => {
  _isMounted.current = true

  return () => {
    _isMounted.current = false
  }
}, [])

【讨论】:

    猜你喜欢
    • 2019-04-14
    • 2020-04-24
    • 1970-01-01
    • 2020-01-23
    • 2021-11-22
    • 1970-01-01
    • 2020-06-03
    • 2021-12-15
    • 2021-07-23
    相关资源
    最近更新 更多