简体   繁体   中英

useContext is not displaying the updated state (when data is retrieved from firebase in useEffect hook in global file) in child component

I am using context api to have a global state of products, for this I have ProductsContext.js

I have an empty state of array defined like this in ProductsContext.js

const [products, setProducts] = useState([]);

In the same file (ProductsContext.js) I am using useEffect to retrive all the products from firebase and then updating the products state. Below is all the code of ProductsContext.js

import React, { createContext, useState, useEffect } from 'react'
import { db } from '../Config/Config'

export const ProductsContext = createContext();

export const ProductsContextProvider = (props) => {
    // console.log(props);
    const [products, setProducts] = useState([]);

    useEffect(() => {
        // console.log('use effect');
        const prevProducts = products;
        db.collection('Products').onSnapshot(snapshot => {
            let changes = snapshot.docChanges();
            changes.forEach(change => {
                if (change.type === 'added') {
                    prevProducts.push({
                        ProductID: change.doc.id,
                        ProductName: change.doc.data().ProductName,
                        ProductDescription: change.doc.data().ProductDescription,
                        ProductPrice: change.doc.data().ProductPrice,
                        ProductImg: change.doc.data().ProductImg
                    })
                }
                // console.log(prevProducts);
                setProducts(prevProducts);
                console.log(products); // I am able to see the products in console
            })
        })
    })

    return (
        <ProductsContext.Provider value={{ products: [...products] }}>
            {props.children}
        </ProductsContext.Provider>
    )
}

My problem is Home.js is my child component and I am using useContext in this file but it is not returning the updated state but only returning an empty array

import React, { useContext } from 'react'
import { ProductsContext } from '../Global/ProductsContext'

export const Home = () => {
    const { products } = useContext(ProductsContext);
    console.log(products); // empty array
    return (
        <div>

        </div>
    )
}

this is my app.js

import React, { Component } from 'react'
import { ProductsContextProvider } from './Global/ProductsContext'
import { Home } from './Components/Home'

export class App extends Component {
    render() {
        return (
            <ProductsContextProvider>
                <Home />
            </ProductsContextProvider>
        )
    }
}

export default App

Whenever your state is an object (or an array, but that's technically also an object), you must avoid mutating it.

In your code, you are mutating your products array, by calling push(...) on the original array instead of creating a copy of it.

Setting the state with a mutated array does not trigger an update because React compares the reference of the next array with the previous array and determines it to be equal.

To solve this, create a copy of the array and update the state with that:

const prevProducts = [...products];

I don't know how but when I changed my functional component (ProductsContext.js) to class based component with the same approach, it is somehow working and I am able to get the products in my child components. Here is the code

import React, { createContext } from 'react'
import { db } from '../Config/Config'

export const ProductsContext = createContext();

export class ProductsContextProvider extends React.Component {

    state = {
        products: []
    }

    componentDidMount() {

        const prevProducts = this.state.products;
        db.collection('Products').onSnapshot(snapshot => {
            let changes = snapshot.docChanges();
            changes.forEach(change => {
                if (change.type === 'added') {
                    prevProducts.push({
                        ProductID: change.doc.id,
                        ProductName: change.doc.data().ProductName,
                        ProductDescription: change.doc.data().ProductDescription,
                        ProductPrice: change.doc.data().ProductPrice,
                        ProductImg: change.doc.data().ProductImg
                    })
                }
                // console.log(prevProducts);
                this.setState({
                    products: prevProducts
                })
                // console.log(this.state.products);
            })
        })

    }
    render() {
        return (
            <ProductsContext.Provider value={{ products: [...this.state.products] }}>
                {this.props.children}
            </ProductsContext.Provider>
        )
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM