【问题标题】:ReactJS: useEffect does not set correct length of Redux store arrayReactJS:useEffect 没有设置正确的 Redux 存储数组长度
【发布时间】:2021-01-26 18:57:35
【问题描述】:

我有一种情况,我正在填充几个 material-ui 卡组件,其中包含与登录用户相关联的账单数量、在工作上花费的总小时数等。我有一个带有 SummaryCard 子项的 Summary 组件,它将一个值作为道具然后显示它:

/** @jsxImportSource @emotion/react */
import React, { Fragment, useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles'
import { SectionTitle, SummaryCard } from '../../atoms'
import { css } from '@emotion/react'

const styles = makeStyles({
    summaryRow: {
        borderBottom: '3px solid #2c3e50',
        margin: '0 auto',
        padding: '0 2rem 1rem',
        width: '84vw'
    }
})

const Summary = () => {
    const classes = styles()
    let billsData = useSelector(state => state.dashboardData.billsData)
    let [billsCount, setBillsCount] = useState(3)

    useEffect(() => {
        console.log(billsData)
        console.log(billsData.data)
        console.log(billsData.data.length)
        setBillsCount(billsData.data.length)
    },[billsData])
  
    return (
        <Fragment> 
            <section css={summaryCSS} >
                <SectionTitle title='SUMMARY' />
                    <Grid classes={{ root: classes.summaryRow }} container spacing={4} >
                        <Grid item xs={3} >
                            <SummaryCard color='#4E73DF' title='NUMBER OF BILLS' type='Receipt' value={billsCount} />
                        </Grid>
                        <Grid item xs={3} >
                            <SummaryCard color='#f6c23e' title='HOURS BILLED' type='Hours' value={0} />
                        </Grid>
                        <Grid item xs={3} >
                            <SummaryCard color='#1CC88A' title='TOTAL EXPENSES' type='Dollar' value={0} />
                        </Grid>
                        <Grid item xs={3} >
                            <SummaryCard color='#E74A3B' title='CUSTOMERS SERVED' type='Customers' value={0} />
                        </Grid>
                    </Grid>
            </section>
        </Fragment>
    )
}

export default Summary

我已将 Redux 存储数据设置为等于 billsData(通过 useSelector),其想法是在 billsData 更新时利用 useEffect 挂钩设置 billsCount 的状态。但是,setBillsCount(billsData.data.length) 行将计数设置为 0,即使我已经在控制台中记录了该数组不为空的证明:

更糟糕的是,setBillsCount 正在覆盖我的初始 billsCount 值,我已将其设置为 3 以证明 useEffect 实际上按预期触发,但未设置正确的值。然后 SummaryCard 组件使用新的 0 值重新渲染,如下所示:

最后,这是 React 组件的屏幕截图,显示选择器已填充但状态仍为 0,并且 Redux 存储已填充,正如预期的那样:

那么为什么 setBillsCount 会将我的 billsCount 覆盖为 0?此外,如果我在编辑器中进行更改并重新保存以更新我的 localhost 实例,则会加载正确的值 (18),如下所示:

我已经尝试了几件事,包括:1)添加一个 initialRender 状态和依赖项,这样 useEffect 会在 initalRender 等于 0 时递增以触发重新渲染,2)创建一个新数组,将 billsData.data 数组推入它,并尝试使用 newArray.length 设置 billsCount,3)实现一个临时计数变量并在数组上运行 .forEach 循环以在使用它设置状态之前增加计数,以及 4)将 billsData 传递给 Summary 组件作为来自父组件的 {props}。

更新:在检查了我的 Redux 操作/reducers 之后,我觉得我好像在遵循推荐的结构,但我不确定:

export const addUser = user => ({
    type: 'ADD_USER',
    payload: user
})

export const addActivityData = data => ({
    type: 'ADD_ACTIVITY_DATA',
    payload: data
})

export const addBillsData = data => ({
    type: 'ADD_BILLS_DATA',
    payload: data
})

export const addCustomerData = data => ({
    type: 'ADD_CUSTOMER_DATA',
    payload: data
})

export const addJobsData = data => ({
    type: 'ADD_JOBS_DATA',
    payload: data
})

export const addWorkerData = data => ({
    type: 'ADD_WORKER_DATA',
    payload: data
})
dataReducer.js

const initialState = {}

export default function dataReducer (state = initialState, action) {
    switch (action.type) {
        case 'ADD_ACTIVITY_DATA': {
            return Object.assign({}, state, {
                activityData: action.payload
            })
        }
        case 'ADD_BILLS_DATA': {
            return Object.assign({}, state, {
                billsData: action.payload
            })
        }
        case 'ADD_CUSTOMER_DATA': {
            return Object.assign({}, state, {
                customerData: action.payload
            })
        }
        case 'ADD_JOBS_DATA': {
            return Object.assign({}, state, {
                jobsData: action.payload
            })
        }
        case 'ADD_WORKER_DATA': {
            return Object.assign({}, state, {
                workerData: action.payload
            })
        }
        default:
            return state
    }
}

【问题讨论】:

  • 也许你在reducer中改变state.dashboardData.billsData?我看不到您如何有 2 个日志显示 18 的数组,然后将长度记录为 0。也许尝试记录:console.log('data at this moment:',JSON.stringify(billsData.data,undefined,2))
  • 我不知道为什么会发生这种情况,但我也不明白为什么您将计数保存到状态而不是直接使用计数? &lt;SummaryCard color='#4E73DF' title='NUMBER OF BILLS' type='Receipt' value={billsData.data.length} /&gt;
  • @LindaPaiste 是我的错,我忘了说我也已经尝试过了,结果相同。我之所以选择使用 useState/useEffect 设置状态的原因是,当这种方法失败时,我认为我必须调用 useEffect 来更改子道具并重新渲染组件。
  • @HMR 这是个好主意,谢谢,我会看看我是否正在改变数据。至于你的第二个建议,我试过了,结果一样:data at this moment: [].
  • @ChardBrez 这不是相同的结果,但是在您发布的图像中它显示Array(18);当您尝试将其记录为字符串时,您会得到一个空数组,当记录长度时,您会得到 0。这意味着在某些时候您会在该日志之后对其进行变异。

标签: javascript reactjs react-redux


【解决方案1】:

好的,我认为我已将问题跟踪到我的 Firebase 调用实施。我没有将我所有的 Firebase/Firestore 'get' 函数存储在一个单独的 Firebase 类中,并通过使用这个 HOC 包装我的组件来访问它们,而是简化了流程,只需获取 Firebase 数据并将其直接发送到 Redux 商店。与 useEffect 挂钩:

 useEffect(() => {
        
        const addUserToStore = async () => {
            dispatch(allActions.userActions.addUser(userState))
        }

        const getBills = async (id) => {
            firestore.collection('Bill Fanout').where('Client', '==', `${id}`).onSnapshot( function (querySnapshot) {
                let billsArr : any[] = []
        
                querySnapshot.forEach(doc => {
                    billsArr.push(doc.data())
                })
                
                dispatch(allActions.dataActions.addBillsCount(billsArr.length))
                dispatch(allActions.dataActions.addBillsData(billsArr))
            })
        }

        const getCustomers = async (id) => {
            db.child('Customer Fanout').child(id).on('value', snapshot => {
                const custArr : any[] = []
                const obj = snapshot.val()

                for (let prop in obj) {
                    let innerObj = {}
                    innerObj[prop] = obj[prop]
                    custArr.push(innerObj)
                }

                dispatch(allActions.dataActions.addCustomerCount(custArr.length))
                dispatch(allActions.dataActions.addCustomerData(custArr))
            })
        }

        const getJobs = async (id) => {
            db.child('Jobs Board Fanout').child(id).on('value', snapshot => {
                const jobArr : any[] = []

                snapshot.forEach(doc => {
                    jobArr.push(doc.val())
                })

                dispatch(allActions.dataActions.addJobsCount(jobArr.length))
                dispatch(allActions.dataActions.addJobsData(jobArr))
            })
        }

        if (userState.isLoggedIn) {
            addUserToStore()
            // Gets the bills data
            getBills(userState.uid)
            getCustomers(userState.uid)
            getJobs(userState.uid)
                .then(() => setDataAddedToStore(true))       
        }


    }, [userState.isLoggedIn])

现在我的子组件中的 useEffect 挂钩正确地更新了组件状态,这些状态直接作为道具传递到最终组件中。

【讨论】:

    猜你喜欢
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 2020-10-28
    • 1970-01-01
    • 2012-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多