admin管理员组

文章数量:1402786

I'm trying to learn how to use React's Context API as well as Hooks, and trying to work on a project where I'm using fetch().

When using both, I get the request, but I can't get the data I set, except for loading.

import React,{useState, createContext} from 'react';

export const ProductsContext = createContext();

export const ProductsProvider = props => {
    const [categories, setCategories] = useState({ categories: {} });
    const [products, setProducts] = useState({ products: {} });
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        (async () => {
            const [categoriesResult, productsResult] = await Promise.all([fetch('http://localhost:3000/api/categories'), fetch('http://localhost:3000/api/products')]);
            setCategories(categoriesResult);
            setProducts(productsResult);
            setLoading(false);
          })();
        }, []);
    return (
    <ProductsContext.Provider value={ { products, categories, loading } }>
        {props.children}
    </ProductsContext.Provider>
    );
}

Here I'm creating a context where I'd like to pass products (and categories) through the Provider.

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


const Products = () => {
    const {products, categories, loading} = useContext(ProductsContext);
    return (
        <div className="products">
            { console.log(products, categories, loading) }
        </div>
    ); 
};

Page

import React from 'react';
import Header from './Header';
import Banner from './Banner';
import ProductsContainer from './Products/ProductsContainer';
import {ProductsProvider} from './Products/ProductsContext';
const Page = () => (

    <>
      <Header/>
      <Banner/>
      <ProductsProvider>
        <ProductsContainer/>
      </ProductsProvider>
    </>

);

export default Page;

This is the results I get via console.log:

{products: {…}} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} true
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} true
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false

As you can see, there is no data, but loading changes.

What am I doing wrong? How can I pass products via Context API?

I'm trying to learn how to use React's Context API as well as Hooks, and trying to work on a project where I'm using fetch().

When using both, I get the request, but I can't get the data I set, except for loading.

import React,{useState, createContext} from 'react';

export const ProductsContext = createContext();

export const ProductsProvider = props => {
    const [categories, setCategories] = useState({ categories: {} });
    const [products, setProducts] = useState({ products: {} });
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        (async () => {
            const [categoriesResult, productsResult] = await Promise.all([fetch('http://localhost:3000/api/categories'), fetch('http://localhost:3000/api/products')]);
            setCategories(categoriesResult);
            setProducts(productsResult);
            setLoading(false);
          })();
        }, []);
    return (
    <ProductsContext.Provider value={ { products, categories, loading } }>
        {props.children}
    </ProductsContext.Provider>
    );
}

Here I'm creating a context where I'd like to pass products (and categories) through the Provider.

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


const Products = () => {
    const {products, categories, loading} = useContext(ProductsContext);
    return (
        <div className="products">
            { console.log(products, categories, loading) }
        </div>
    ); 
};

Page

import React from 'react';
import Header from './Header';
import Banner from './Banner';
import ProductsContainer from './Products/ProductsContainer';
import {ProductsProvider} from './Products/ProductsContext';
const Page = () => (

    <>
      <Header/>
      <Banner/>
      <ProductsProvider>
        <ProductsContainer/>
      </ProductsProvider>
    </>

);

export default Page;

This is the results I get via console.log:

{products: {…}} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} true
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} true
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false
Products.js:9 Response {type: "basic", url: "http://localhost:3000/api/products", redirected: false, status: 200, ok: true, …} Response {type: "basic", url: "http://localhost:3000/api/categories", redirected: false, status: 200, ok: true, …} false

As you can see, there is no data, but loading changes.

What am I doing wrong? How can I pass products via Context API?

Share Improve this question asked Sep 8, 2019 at 21:24 kinxkinx 4935 gold badges15 silver badges33 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 4

At casual reading, the React.useContext() looks serviceable. The issue I see is that the React state variables, categories and product are being set to the Response from fetch() and according to MDN's fetch() reference, fetch returns a value that resolves from a Promise to a Response, which is not quite what you want.

But this is close! You should do something like

  setCategories(categoriesResult.json());
  setProducts(productsResult.json());

or figure out another way to massage the Response.Body into a form that you can manipulate. Check out the API reference for Response


To resolve the promise, you call promise.then(...) where promise is the Promise object returned from fetch().

To that end, I would suggest separating the two fetches in order to make resolving the promises simple and possibly make other parts of your interface respond sooner (i.e. if one fetch es in before the other, you could render those results even if you don't have the other. Even if you want to wait until both parts are plete, you could make your rendering contingent on loading's state.)

export const ProductsProvider = props => {
  const [loading, setLoading] = useState(true);

  const [categories, setCategories] = useState({});
  useEffect(() => {
    fetch('http://localhost:3000/api/categories')
      .then((response) => {
         setCategories(response.json());
      }
    setLoading(/* false only if categories AND product are not {} */);
  }, []);

  // Repeat the above, except for products

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

本文标签: javascriptUsing useEffect and useContext while fetching dataStack Overflow