如何在 const 组件中分配来自 Http 响应的数据?
How can I assign data from Http response in const Component?
我的目标是最终使用上下文 API 创建添加购物车功能。为此,我需要从数据库中获取产品并将其存储在数组中。
目前,我面临的挑战是如何从 axios 响应中检索数据并将其存储在将在 const 组件中传递的变量中。显然,问题是在 Axios 响应完成之前,变量被传递给了 child。
我尝试使用 await
关键字,但收到关于不在 async
函数中的错误。因此,我尝试插入 async
关键字但是没有用,因为它产生了错误。
我能够成功地从 class 组件中的 axios 检索数据,但是,我无法在这些 const
.
中这样做
这是我的代码:
Context.js
import { createContext, useContext, useReducer } from "react";
import React from "react";
import ProductService from "../services/ProductService";
import { cartReducer } from "./Reducers";
const Cart = createContext();
const Context = ({ children }) => {
let products = [];
console.log("part1", products);
ProductService.getAllProducts().then((res) => {
products = res.data; //Also tried setState({ products : res.data})
console.log("response: ", products);
});
const [state, dispatch] = useReducer(cartReducer, {
products: products,
cart: [],
});
return <Cart.Provider value={{ state, dispatch }}>{children}</Cart.Provider>;
};
export const CartState = () => {
return useContext(Cart);
};
export default Context;
ProductService.js
import axios from "axios";
const PRODUCT_BASE_URL = "http://localhost:8080/api/v1/";
class ProductService {
getAllProducts() {
return axios.get(PRODUCT_BASE_URL + "products");
}
getProductsByCategory(category) {
return axios.get(PRODUCT_BASE_URL + "products/" + category);
}
getProductById(id) {
return axios.get(PRODUCT_BASE_URL + "product/" + id);
}
}
export default new ProductService();
Reducers.js
export const cartReducer = (state, action) => {
switch (action.type) {
default:
return state;
}
};
HomeComponenet.jsx
import React from "react";
import SlideShowComponent from "./SlideShowComponent";
import HomeCategoriesComponent from "./HomeCategoriesComponent";
import FeaturedProductsComponent from "./FeaturedProductsComponent";
import { CartState } from "../context/Context";
// class HomeComponent extends React.Component
function HomeComponent() {
const { state } = CartState();
console.log("Cart Inside the Home Component: ", state);
return (
<>
<SlideShowComponent />
<div>
<HomeCategoriesComponent />
</div>
<div>
<FeaturedProductsComponent />
</div>
</>
);
}
export default HomeComponent;
首先,我在这里创建了一个固定示例:https://stackblitz.com/edit/react-jqtcia?file=src/Context.js
你是对的,你需要 await
来自 axios 的结果。在 React 中,我们使用 useEffect 钩子来处理有副作用的事情或者不应该作为渲染的一部分完成的事情。 React 中的渲染应该是非阻塞的,也就是说它们不应该依赖于数据获取之类的东西。
一个简单的例子就是如果我们需要它在本地状态。此示例在没有数据的情况下呈现,然后 re-renders 一旦数据可用。
const [products, setProducts] = useState([]);
useEffect(async () => {
const { data } = await ProductService.getAllProducts();
setProducts(data);
}, []);
return <div>{products.length > 0 ? `${products.length} products` : 'Loading...'</div>
注意: , []);
意味着这将在第一次渲染发生时触发一次。
这解决了问题的第一部分,从 request/axios 中得到结果。
然而,第二部分也是最重要的部分是您没有使用这个值。您试图将结果作为初始状态的一部分插入,但在创建时它是空的。当您使用 reducer (useReducer) 时,这意味着您需要为每个事件分派一个操作并处理所有相关事件以在 reducer 中获取数据。这意味着,您应该需要能够处理:
- 一些数据处于加载状态(例如,分页或首次加载)
- 数据加载失败
- 数据已部分加载
- 数据已加载完毕
我创建了一个简单的快乐示例(没有分页并且数据获取总是成功):
Context.js
useEffect(async () => {
const { data: loadedProducts } = await ProductService.getAllProducts();
console.log('response: ', JSON.stringify(loadedProducts));
dispatch({ type: 'PRODUCTS_LOADED', products: loadedProducts });
console.log(state);
}, []);
Reducers.js
export const cartReducer = (state, action) => {
console.log(action);
switch (action.type) {
case 'PRODUCTS_LOADED':
const newState = { ...state, products: action.products };
console.log(newState);
return newState;
default:
return state;
}
};
事实上,如果您使用原始代码完成此操作,它就会起作用:
ProductService.getAllProducts().then((res) => {
dispatch({ type: 'PRODUCTS_LOADED', products : res.data});
console.log("response: ", products);
});
但是,这有一个不同的错误:它会重新获取数据,然后在每次 Context.js 为 re-rendered 时发送事件。
鉴于您在评论中要求提供更多信息,我将在此处提供(评论不够大)。
我已经为上面的钩子链接了相关的 API 文档,这些提供了很好的信息。 React 很好地解释了这些钩子(以及库本身)的内容和原因。说真的,如果你还没有读过他们的 documentation,你应该读一读。
其他资源:
What, when and how to use useEffect - 简而言之,如果您要做的事情不是渲染,例如数据获取,它是 side-effect 并且应该在 useEffect
中。
What is a reducer in JavaScript/React/Redux - Reducers 是一种使共享状态更易于管理和测试的模式。基本思想是定义一个 reducer
,它接受一个初始状态和一个 event/action,并产生一个新状态。关键思想是必须没有 side-effects,相同的动作和状态将始终产生相同的结果,无论 date/time、网络状态等。这使得测试和推理事情变得更简单,但代价是更复杂的状态管理。
但是,我在您最初的问题中忽略了一些重要的事情,那就是您在这里是在重新发明轮子。已经有一个库可以集中你的状态并通过上下文使其可用,它是最初发明 reducer 的库:redux. I'm guessing you have read something like this article about using context instead of redux,但是 redux 对你的好处是有大量关于如何使用的文档它并解决这些问题。
我的建议是确保you need/want redux/reducers。它有它的价值,我个人喜欢这种模式,但如果你刚刚开始使用 React,我认为你最好只使用 useState
。
我的目标是最终使用上下文 API 创建添加购物车功能。为此,我需要从数据库中获取产品并将其存储在数组中。
目前,我面临的挑战是如何从 axios 响应中检索数据并将其存储在将在 const 组件中传递的变量中。显然,问题是在 Axios 响应完成之前,变量被传递给了 child。
我尝试使用 await
关键字,但收到关于不在 async
函数中的错误。因此,我尝试插入 async
关键字但是没有用,因为它产生了错误。
我能够成功地从 class 组件中的 axios 检索数据,但是,我无法在这些 const
.
这是我的代码:
Context.js
import { createContext, useContext, useReducer } from "react";
import React from "react";
import ProductService from "../services/ProductService";
import { cartReducer } from "./Reducers";
const Cart = createContext();
const Context = ({ children }) => {
let products = [];
console.log("part1", products);
ProductService.getAllProducts().then((res) => {
products = res.data; //Also tried setState({ products : res.data})
console.log("response: ", products);
});
const [state, dispatch] = useReducer(cartReducer, {
products: products,
cart: [],
});
return <Cart.Provider value={{ state, dispatch }}>{children}</Cart.Provider>;
};
export const CartState = () => {
return useContext(Cart);
};
export default Context;
ProductService.js
import axios from "axios";
const PRODUCT_BASE_URL = "http://localhost:8080/api/v1/";
class ProductService {
getAllProducts() {
return axios.get(PRODUCT_BASE_URL + "products");
}
getProductsByCategory(category) {
return axios.get(PRODUCT_BASE_URL + "products/" + category);
}
getProductById(id) {
return axios.get(PRODUCT_BASE_URL + "product/" + id);
}
}
export default new ProductService();
Reducers.js
export const cartReducer = (state, action) => {
switch (action.type) {
default:
return state;
}
};
HomeComponenet.jsx
import React from "react";
import SlideShowComponent from "./SlideShowComponent";
import HomeCategoriesComponent from "./HomeCategoriesComponent";
import FeaturedProductsComponent from "./FeaturedProductsComponent";
import { CartState } from "../context/Context";
// class HomeComponent extends React.Component
function HomeComponent() {
const { state } = CartState();
console.log("Cart Inside the Home Component: ", state);
return (
<>
<SlideShowComponent />
<div>
<HomeCategoriesComponent />
</div>
<div>
<FeaturedProductsComponent />
</div>
</>
);
}
export default HomeComponent;
首先,我在这里创建了一个固定示例:https://stackblitz.com/edit/react-jqtcia?file=src/Context.js
你是对的,你需要 await
来自 axios 的结果。在 React 中,我们使用 useEffect 钩子来处理有副作用的事情或者不应该作为渲染的一部分完成的事情。 React 中的渲染应该是非阻塞的,也就是说它们不应该依赖于数据获取之类的东西。
一个简单的例子就是如果我们需要它在本地状态。此示例在没有数据的情况下呈现,然后 re-renders 一旦数据可用。
const [products, setProducts] = useState([]);
useEffect(async () => {
const { data } = await ProductService.getAllProducts();
setProducts(data);
}, []);
return <div>{products.length > 0 ? `${products.length} products` : 'Loading...'</div>
注意: , []);
意味着这将在第一次渲染发生时触发一次。
这解决了问题的第一部分,从 request/axios 中得到结果。
然而,第二部分也是最重要的部分是您没有使用这个值。您试图将结果作为初始状态的一部分插入,但在创建时它是空的。当您使用 reducer (useReducer) 时,这意味着您需要为每个事件分派一个操作并处理所有相关事件以在 reducer 中获取数据。这意味着,您应该需要能够处理:
- 一些数据处于加载状态(例如,分页或首次加载)
- 数据加载失败
- 数据已部分加载
- 数据已加载完毕
我创建了一个简单的快乐示例(没有分页并且数据获取总是成功):
Context.js
useEffect(async () => {
const { data: loadedProducts } = await ProductService.getAllProducts();
console.log('response: ', JSON.stringify(loadedProducts));
dispatch({ type: 'PRODUCTS_LOADED', products: loadedProducts });
console.log(state);
}, []);
Reducers.js
export const cartReducer = (state, action) => {
console.log(action);
switch (action.type) {
case 'PRODUCTS_LOADED':
const newState = { ...state, products: action.products };
console.log(newState);
return newState;
default:
return state;
}
};
事实上,如果您使用原始代码完成此操作,它就会起作用:
ProductService.getAllProducts().then((res) => {
dispatch({ type: 'PRODUCTS_LOADED', products : res.data});
console.log("response: ", products);
});
但是,这有一个不同的错误:它会重新获取数据,然后在每次 Context.js 为 re-rendered 时发送事件。
鉴于您在评论中要求提供更多信息,我将在此处提供(评论不够大)。 我已经为上面的钩子链接了相关的 API 文档,这些提供了很好的信息。 React 很好地解释了这些钩子(以及库本身)的内容和原因。说真的,如果你还没有读过他们的 documentation,你应该读一读。
其他资源:
What, when and how to use useEffect - 简而言之,如果您要做的事情不是渲染,例如数据获取,它是 side-effect 并且应该在 useEffect
中。
What is a reducer in JavaScript/React/Redux - Reducers 是一种使共享状态更易于管理和测试的模式。基本思想是定义一个 reducer
,它接受一个初始状态和一个 event/action,并产生一个新状态。关键思想是必须没有 side-effects,相同的动作和状态将始终产生相同的结果,无论 date/time、网络状态等。这使得测试和推理事情变得更简单,但代价是更复杂的状态管理。
但是,我在您最初的问题中忽略了一些重要的事情,那就是您在这里是在重新发明轮子。已经有一个库可以集中你的状态并通过上下文使其可用,它是最初发明 reducer 的库:redux. I'm guessing you have read something like this article about using context instead of redux,但是 redux 对你的好处是有大量关于如何使用的文档它并解决这些问题。
我的建议是确保you need/want redux/reducers。它有它的价值,我个人喜欢这种模式,但如果你刚刚开始使用 React,我认为你最好只使用 useState
。