REACT- firebase的上下文API

发布于 2025-02-11 04:38:19 字数 3948 浏览 2 评论 0原文

首先,我想说a)我是新手反应,b)我花了大约8个小时的时间试图在stackoverflow中挖掘修复程序,尝试了许多不同的方法。我被困。非常感谢您的时间。

此处的目的是等待Firebase的请求在渲染页面之前创建值“产品”。持续发生的是产品被加载为无效。

这是我的设置:

productContext.js

import React, {createContext, useState, useEffect} from 'react';
import { dummyProducts } from '../services/dummy';
import {collection, onSnapshot} from "firebase/firestore";
import {db} from "../firebase";

export const ProductsContext = createContext();

export function getProductData() {
    const myArray =[];
    return new Promise(function (resolve, reject) {
        const querySnapshot = onSnapshot(collection(db, 'products'), (snapshot) => {
            snapshot.forEach(doc => {
                let productData = doc.data();
                myArray.push(productData)
            })
        })
        resolve(myArray);
    });
}

const ProductsContextProvider = ({children}) => {

    const [products, setProducts] = useState([]);
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        let didCancel = false;
        async function fetchMyAPI() {
            let results = await getProductData();
            console.log(results) // Outputs array with results
            setProducts(results)
        }
        fetchMyAPI();
        return () => { didCancel = true; }; // Remember if we start fetching something else
    }, []);

    return (
        <ProductsContext.Provider value={{products}}>
            { children }
        </ProductsContext.Provider>
    );
}

export default ProductsContextProvider;

useproducts.js

// eslint-disable-next-line
import React, { useContext } from 'react';
import { ProductsContext } from '../contexts/ProductsContext';

export const useProducts = () => {

    const ctx = useContext(ProductsContext)

    return {
        ...ctx
    }
}

productsgrid.js

import React from 'react';
import ProductItem from './ProductItem';
import styles from './ProductsGrid.module.scss';
import { useProducts } from '../../hooks/useProducts';

const ProductsGrid = () => {

    const {products} = useProducts();
    console.log(products.length) // Outputs zero
    console.log(products) // Outputs array with 1 object (correct)

    return ( 
        <div className={styles.p__container}>
            <div className="row">
                <div className="col-sm-8">
                    <div className="py-3">
                        {products.length} Products
                    </div>
                </div>
                <div className="col-sm-4">

                </div>
            </div>
            <div className={styles.p__grid}>

                {
                    products.map(product => (
                        <ProductItem key={product.id} product={product}/>
                    ))
                }

            </div>
            <div className={styles.p__footer}>

            </div>
        </div>
     );
}
 
export default ProductsGrid;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import Routes from './routes';
import * as serviceWorker from './serviceWorker';

import { HelmetProvider } from 'react-helmet-async';
import ProductsContextProvider from './contexts/ProductsContext';
import CartContextProvider from './contexts/CartContext';

ReactDOM.render(
    <HelmetProvider>
      <ProductsContextProvider>
        <CartContextProvider>
          <Routes />
        </CartContextProvider>
      </ProductsContextProvider>
    </HelmetProvider>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();

First I want to say a) I am new to react, and b) I spent about 8hrs trying to dig around in stackoverflow for a fix, trying many different methods. I am stuck. Many thanks for your time.

The objective here is to wait for the request from firebase to create the value 'products', before rendering the page. What keeps happening is products gets loaded as null.

Here is my setup:

ProductContext.js

import React, {createContext, useState, useEffect} from 'react';
import { dummyProducts } from '../services/dummy';
import {collection, onSnapshot} from "firebase/firestore";
import {db} from "../firebase";

export const ProductsContext = createContext();

export function getProductData() {
    const myArray =[];
    return new Promise(function (resolve, reject) {
        const querySnapshot = onSnapshot(collection(db, 'products'), (snapshot) => {
            snapshot.forEach(doc => {
                let productData = doc.data();
                myArray.push(productData)
            })
        })
        resolve(myArray);
    });
}

const ProductsContextProvider = ({children}) => {

    const [products, setProducts] = useState([]);
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        let didCancel = false;
        async function fetchMyAPI() {
            let results = await getProductData();
            console.log(results) // Outputs array with results
            setProducts(results)
        }
        fetchMyAPI();
        return () => { didCancel = true; }; // Remember if we start fetching something else
    }, []);

    return (
        <ProductsContext.Provider value={{products}}>
            { children }
        </ProductsContext.Provider>
    );
}

export default ProductsContextProvider;

UseProducts.js

// eslint-disable-next-line
import React, { useContext } from 'react';
import { ProductsContext } from '../contexts/ProductsContext';

export const useProducts = () => {

    const ctx = useContext(ProductsContext)

    return {
        ...ctx
    }
}

ProductsGrid.js

import React from 'react';
import ProductItem from './ProductItem';
import styles from './ProductsGrid.module.scss';
import { useProducts } from '../../hooks/useProducts';

const ProductsGrid = () => {

    const {products} = useProducts();
    console.log(products.length) // Outputs zero
    console.log(products) // Outputs array with 1 object (correct)

    return ( 
        <div className={styles.p__container}>
            <div className="row">
                <div className="col-sm-8">
                    <div className="py-3">
                        {products.length} Products
                    </div>
                </div>
                <div className="col-sm-4">

                </div>
            </div>
            <div className={styles.p__grid}>

                {
                    products.map(product => (
                        <ProductItem key={product.id} product={product}/>
                    ))
                }

            </div>
            <div className={styles.p__footer}>

            </div>
        </div>
     );
}
 
export default ProductsGrid;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import Routes from './routes';
import * as serviceWorker from './serviceWorker';

import { HelmetProvider } from 'react-helmet-async';
import ProductsContextProvider from './contexts/ProductsContext';
import CartContextProvider from './contexts/CartContext';

ReactDOM.render(
    <HelmetProvider>
      <ProductsContextProvider>
        <CartContextProvider>
          <Routes />
        </CartContextProvider>
      </ProductsContextProvider>
    </HelmetProvider>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦开始←不甜 2025-02-18 04:38:19

使它起作用。

我将

productionContext.js

import React, { createContext, useState } from 'react';
// import { dummyProducts } from '../services/dummy';
export const ProductsContext = createContext();

const ProductsContextProvider = ({children}) => {

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

    return ( 
        <ProductsContext.Provider value={{products, setProducts}} >
            { children }
        </ProductsContext.Provider>
     );
}

export default ProductsContextProvider;

和更改

为:

import React, { useEffect, useState, createContext } from 'react';
import ProductItem from './ProductItem';
import styles from './ProductsGrid.module.scss';
import { useProducts } from '../../hooks/useProducts'
import {collection, onSnapshot, where} from "firebase/firestore";
import {db} from "../../firebase";

export const ProductsContext = createContext();

async function getProductData() {
    const myArray =[];
    const promise = new Promise(resolve =>
    onSnapshot(collection(db, 'products'), (snapshot) => {
        snapshot.forEach(doc => {
            let productData = doc.data();
            myArray.push(productData)
        })
            resolve(myArray)
    }));

    const productData = await promise;
    //console.log(productData);
    return productData;
}

const ProductsGrid = () => {

    const { products, setProducts } = useProducts()
    const [loading, setLoading]= useState(false);

    const getProducts = async ()=>{
        setLoading(true)
        const newText = await getProductData();
        setProducts(newText)
        setLoading(false)
    }

    useEffect(()=> {
        getProducts()
    },[])

    return (
        loading ? (
            <div className={styles.p__container}>
                <div className="row">
                    <div className="col-sm-8"> <h4>Loading...</h4></div></div></div>) : (
        <div className={styles.p__container}>
            <div className="row">
                <div className="col-sm-8">
                    <div className="py-3">
                        {products.length} Products
                    </div>
                </div>
                <div className="col-sm-4">

                </div>
            </div>
            <div className={styles.p__grid}>

                {
                    products.map(product => (
                        <ProductItem key={product.id} product={product}/>
                    ))
                }

            </div>
            <div className={styles.p__footer}>

            </div>
        </div>
        ));
}
 
export default ProductsGrid;

著名的更改为:

  1. 我将获取请求移至productsGrid
  2. 我更改了GetProductData()函数的结构。我怀疑这是新承诺的“回报”,这并没有带来预期的效果。
  3. 我添加了加载状态

Got it working.

I changed

ProductsContext.js

import React, { createContext, useState } from 'react';
// import { dummyProducts } from '../services/dummy';
export const ProductsContext = createContext();

const ProductsContextProvider = ({children}) => {

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

    return ( 
        <ProductsContext.Provider value={{products, setProducts}} >
            { children }
        </ProductsContext.Provider>
     );
}

export default ProductsContextProvider;

And changed

ProductsGrid.js to:

import React, { useEffect, useState, createContext } from 'react';
import ProductItem from './ProductItem';
import styles from './ProductsGrid.module.scss';
import { useProducts } from '../../hooks/useProducts'
import {collection, onSnapshot, where} from "firebase/firestore";
import {db} from "../../firebase";

export const ProductsContext = createContext();

async function getProductData() {
    const myArray =[];
    const promise = new Promise(resolve =>
    onSnapshot(collection(db, 'products'), (snapshot) => {
        snapshot.forEach(doc => {
            let productData = doc.data();
            myArray.push(productData)
        })
            resolve(myArray)
    }));

    const productData = await promise;
    //console.log(productData);
    return productData;
}

const ProductsGrid = () => {

    const { products, setProducts } = useProducts()
    const [loading, setLoading]= useState(false);

    const getProducts = async ()=>{
        setLoading(true)
        const newText = await getProductData();
        setProducts(newText)
        setLoading(false)
    }

    useEffect(()=> {
        getProducts()
    },[])

    return (
        loading ? (
            <div className={styles.p__container}>
                <div className="row">
                    <div className="col-sm-8"> <h4>Loading...</h4></div></div></div>) : (
        <div className={styles.p__container}>
            <div className="row">
                <div className="col-sm-8">
                    <div className="py-3">
                        {products.length} Products
                    </div>
                </div>
                <div className="col-sm-4">

                </div>
            </div>
            <div className={styles.p__grid}>

                {
                    products.map(product => (
                        <ProductItem key={product.id} product={product}/>
                    ))
                }

            </div>
            <div className={styles.p__footer}>

            </div>
        </div>
        ));
}
 
export default ProductsGrid;

Notable changes are:

  1. I moved the fetch request to ProductsGrid
  2. I changed how the getProductData() function was structured. I suspect it was the 'return' on the new Promise which wasn't giving the desired effect.
  3. I added Loading state
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文