【问题标题】:State is null, after useEffect hooks状态为空,在 useEffect 挂钩之后
【发布时间】:2021-09-07 12:24:02
【问题描述】:

我正在尝试通过在 useEffect 中调度方法来设置产品。但是,状态仍然显示为 null。

index.html

import React, { useEffect, Fragment } from "react";
import { useSelector, useDispatch } from "react-redux";

import { fetchProductsData } from "../../store/products-actions";

import Promotion from "./components/Promotion";
import Products from "./components/Products";
import ToastUi from "../../shared/ui/ToastUi";

import { Container, Row, Col } from "react-bootstrap";

const Home = () => {
  const dispatch = useDispatch();

  // const products = useSelector((state) => state.products.products);
  const products = useSelector((state) => state.products.productsTest);
  const cartQuantity = useSelector((state) => state.cart.quantity);

  useEffect(() => {
    dispatch(fetchProductsData());
  }, [dispatch]);

  return (
    <Fragment>
      <ToastUi
        status="Sukses"
        title="Notifikasi"
        message={`(${cartQuantity}) produk baru berhasil di masukkan keranjang`}
      />
      <Container fluid="xl">
        <Row>
          <Col>
            <Promotion />
          </Col>
        </Row>
        <Row className="mt-3" md={3}>
          <Products products={products} />
        </Row>
      </Container>
    </Fragment>
  );
};

export default Home;

Products 在一个周期后仍然显示 null,显然它需要第二个周期才能使该状态发生变化。不知道我怎样才能让它在一个周期内改变。我需要将 useEffect 放在父级中吗?

编辑 如果我添加这个,它将起作用

 {products !== null && <Products products={products} />}
 // {/* <Products products={products} /> */} // 

但是,有没有更好的方法或解释为什么会发生这种情况,谢谢。

编辑

products-slice.js

import { createSlice } from "@reduxjs/toolkit";

import {
  products,
  excel_products,
  product,
  filteredProducts,
  productsTest,
} from "../datafiles";

const initialProductsState = {
  products,
  excel_products,
  product,
  filteredProducts,
  productsTest,
};

const productsSlice = createSlice({
  name: "products",
  initialState: initialProductsState,
  reducers: {
    viewExcelProducts(state, action) {
      state.excel_products = action.payload;
    },
    uploadExcelProducts(state) {
      if (excel_products.length < 0) {
        console.log("error");
      } else {
        const newProducts = state.products.concat(state.excel_products);
        state.products = newProducts;
        state.excel_products = [];
      }
    },
    selectProduct(state, action) {
      const product = state.products.find((item) => item.id === action.payload);
      state.product = product;
    },
    filterProducts(state, action) {
      const filteredProducts = state.products.filter(
        (item) => item.type === action.payload
      );
      state.filteredProducts = filteredProducts;
    },
    setProducts(state, action) {
      state.productsTest = action.payload;
    },
  },
});

export const productsActions = productsSlice.actions;
export default productsSlice;

products-actions.js

import { productsActions } from "./products-slice";

export const fetchProductsData = () => {
  return async (dispatch) => {
    const fetchData = async () => {
      const response = await fetch("http://localhost:5000/products");

      if (!response.ok) {
        throw new Error("Could not fetch data!");
      }
      const data = await response.json();

      return data;
    };
    try {
      const productsData = await fetchData();
      dispatch(productsActions.setProducts(productsData));
    } catch (err) {
      console.log("Error: " + err);
    }
  };
};

【问题讨论】:

  • 你能展示fetchProductsData的动作实现吗?
  • 您的问题中没有足够的信息,您的 fetchProductsData 执行后products 状态值是多少?如果您在return 之前放置console.log(products) 以捕获最后一个值,并将其发布在此处,可能会有所帮助
  • 当然,我已经在上面的第二个编辑语句中添加了代码。此外,当我控制台记录它时,它会产生 2 个不同的日志,一个为空,然后在第二个日志上,值更改为我想要的对象数组
  • 既然您提到检查products !== null 并显示Products,我认为这应该与products 值或Products 中的某些登录错误有关。你也可以发布Products 组件来看看吗?

标签: javascript reactjs react-redux react-hooks


【解决方案1】:

it needs second cycle to make that state changed 是什么意思?

fetchProductsData 是一个异步函数,我假设。这意味着您不会立即收到数据,而是在一段时间后(取决于网络连接速度、有效负载大小等)。因此,您的数据稍后到达是可以的。

异步数据的常用方法是将isLoading 保持在您的状态中。并按如下方式使用:

  const isLoading = useSelector((state) => state.products.isLoading);
  ...
  return (
    <Fragment>
      ...
      {isLoading && <Spinner />} // Some loading indicator
      {!isLoading && <Products products={products} />}
    </Fragment>
  );

这样您将向用户指示正在获取一些数据。这是一个很好的用户体验方法。

isLoading 应该设置在您的 fetchProductsData 操作中的某个位置,如下所示:

export const fetchProductsData = () => {
  return async (dispatch) => {
    ...
    try {
      dispatch(productsActions.setIsLoading(true));
      const productsData = await fetchData();
      dispatch(productsActions.setProducts(productsData));
    } catch (err) {
      console.log("Error: " + err);
    } finally {
      dispatch(productsActions.setIsLoading(false));
    }
  };
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-24
    • 2021-02-11
    • 2019-09-23
    • 2022-01-21
    • 2019-05-07
    • 2019-12-27
    • 1970-01-01
    • 2021-07-18
    相关资源
    最近更新 更多