React 中来自 API 的状态

Posted

技术标签:

【中文标题】React 中来自 API 的状态【英文标题】:State from API in React 【发布时间】:2021-08-03 07:28:20 【问题描述】:

我正在执行一个项目,该项目可以修改产品的价格(从虚假 API 中检索),然后通过点击一个按钮我通过计算 20% 的增值税来更新。我遇到一个问题,我想要一个价格状态,在这个状态下,它是我的条目的值,即 listProduct.price,因此我的 API 的价格最初显示在输入中(即 $105.95对于第一篇文章,...)但它不起作用。

您将首先找到 productsDetails 部分的代码,它显示产品页面和从 API 检索数据的产品代码

如果您有任何我感兴趣的解决方案,请提前致谢。 (抱歉,我是 React 新手,我还在为所有这些概念而苦苦挣扎)

产品详情

import React,  Component  from 'react'
import '../css/ProductsDetails.css'
import AiOutlineArrowLeft from "react-icons/ai";
import Link from 'react-router-dom'


export default class ProductsDetails extends Component 
    constructor(props) 
        super(props);
        this.state = id: this.props.match.params.id, price: ;
    
    updatePrice = (e) => 
        console.log(e);
        this.setState(
            price: e.target.value
        )
    

    render() 
        const location: state: listProduct = this.props;
        return (
            <div className="products__details">
                <Link to="/"><AiOutlineArrowLeft className="nav__arrow" /></Link>
                <h1 className="details__title">listProduct.title</h1>
                <div className="details__align--desk">
                    <div className="details__img">
                    <img className="product__img" src=listProduct.image />
                    </div>
                    <div className="products__align--desk">
                        <h2 className="product__title">Description</h2>
                        <p className="product__description">listProduct.description</p>
                        <h2 className="product__title">Price</h2>
                        <form className="form__price">
                            <input className="input__price" type="text" value=listProduct.price onChange=this.updatePrice />
                            <p>Price (including VAT): Math.round((listProduct.price + listProduct.price * 0.2)*100) /100 €</p>
                            <br/>
                            <input className="btn__update" type="submit" value="Update product" />
                        </form>
                    </div>
                    <div className="category__align--desk">
                        <h2 className="product__title">Category</h2>
                        <p className="product__category">listProduct.category</p>
                    </div>
                </div>
            </div>
        )
     

产品

import React,  Component  from 'react';
import '../css/Products.css';
import axios from 'axios';
import './ProductsDetails'
import Link from 'react-router-dom'

export default class Products extends Component 
    constructor(props) 
      super(props);
      this.state = productsData: [];
    
      componentDidMount = () => 
        axios.get('https://fakestoreapi.com/products?limit=7')
        .then(res => 
          console.log(res.data)
          this.setState (
            productsData: res.data
          )
        )
      
    render() 
        const listsProducts = this.state.productsData.map(listProduct => 
            return <tbody className="products__body">
                    <tr>
                        <td> <Link to=pathname: "/products-details/" + listProduct.id,state: listProduct>listProduct.title</Link></td>
                        <td className="products__category">listProduct.category</td>
                        <td>listProduct.price</td>
                        <td>Math.round((listProduct.price + listProduct.price * 0.2)*100) /100</td>
                    </tr> 
              </tbody>
          )
        return (
            <main className="products">
                <h1 className="products__title">Products management</h1>
                <table cellSpacing="0">
                <thead className="products__head">
                    <tr>
                    <th className="table--title">Product name</th>
                    <th className="table--title">Category</th>
                    <th className="table--title">Price</th>
                    <th className="table--title">Price (including VAT)</th>
                    </tr>
                </thead>
                  listsProducts
                </table>
            </main>
        )
    

【问题讨论】:

抱歉,我将您的代码复制/粘贴到正在运行的 codesandbox 中,并且它运行没有问题(在 `ProductDetails 中提供了有效的初始 price 状态后。到底是什么问题? 感谢您的回答,问题是我无法将 listProduct.price 的初始价格放入我的状态,我该怎么办? 【参考方案1】:

由于您想在一个组件中显示一些数据并从另一个组件更新数据,因此这里的解决方案是Lift State Up。将产品状态和数据提取移至 ProductsDetailsProducts 组件的共同祖先。

建议:

    在父组件中声明productsData、数据获取和updatePrice处理程序。

    function App() 
      const [productsData, setProductsData] = useState([]);
    
      useEffect(() => 
        axios.get("https://fakestoreapi.com/products?limit=7").then((res) => 
          console.log(res.data);
          setProductsData(res.data);
        );
      , []);
    
      const updatePrice = (id, price) => 
        setProductsData((productsData) =>
          productsData.map((product) =>
            product.id === Number(id)
              ? 
                  ...product,
                  price: Number(price)
                
              : product
          )
        );
      ;
    
      return (
        <div className="App">
          <Router>
            <Switch>
              <Route
                path="/products-details/:id"
                render=(props) => (
                  <ProductsDetails
                    products=productsData
                    updatePrice=updatePrice
                    ...props
                  />
                )
              />
              <Route path="/">
                <Products products=productsData />
              </Route>
            </Switch>
          </Router>
        </div>
      );
    
    

    更新 Products 以使用来自父级的 products 属性。

    class Products extends Component 
      render() 
        const listsProducts = this.props.products.map((listProduct) => 
          return (
            <tbody className="products__body" key=listProduct.id>
              <tr>
                <td>
                  " "
                  <Link
                    to=
                      pathname: "/products-details/" + listProduct.id
                    
                  >
                    listProduct.title
                  </Link>
                </td>
                <td className="products__category">listProduct.category</td>
                <td>Number(listProduct.price).toFixed(2)</td>
                <td>
                  Number(listProduct.price * 1.2).toFixed(2) €
                </td>
              </tr>
            </tbody>
          );
        );
    
        return (
          <main className="products">
            <h1 className="products__title">Products management</h1>
            <table cellSpacing="0">
              <thead className="products__head">
                <tr>
                  <th className="table--title">Product name</th>
                  <th className="table--title">Category</th>
                  <th className="table--title">Price</th>
                  <th className="table--title">Price (including VAT)</th>
                </tr>
              </thead>
              listsProducts
            </table>
          </main>
        );
      
    
    

    更新 ProductsDetails 以同时使用 productsupdatePrice 回调属性。为表单创建submitHandler 以更新价格。对于输入,您需要使用 defaultValue 属性,因为您想提供一个初始值,但会立即更改原始数据。

    class ProductsDetails extends Component 
      constructor(props) 
        super(props);
        this.state =  id: this.props.match.params.id, price: 0 ;
      
    
      updatePrice = (e) => 
        console.log(e);
        this.setState(
          price: e.target.value
        );
      ;
    
      submitHandler = (e) => 
        e.preventDefault();
        const 
          match: 
            params:  id 
          
         = this.props;
        this.props.updatePrice(id, this.state.price);
      ;
    
      render() 
        const 
          match: 
            params:  id 
          ,
          products
         = this.props;
    
        const listProduct = products.find((product) => product.id === Number(id));
    
        return (
          <div className="products__details">
            <Link to="/">
              <AiOutlineArrowLeft className="nav__arrow" />
            </Link>
            <h1 className="details__title">listProduct.title</h1>
            <div className="details__align--desk">
              <div className="details__img">
                <img
                  className="product__img"
                  src=listProduct.image
                  
                />
              </div>
              <div className="products__align--desk">
                <h2 className="product__title">Description</h2>
                <p className="product__description">listProduct.description</p>
                <h2 className="product__title">Price</h2>
                <form className="form__price" onSubmit=this.submitHandler>
                  <input
                    className="input__price"
                    type="text"
                    defaultValue=Number(listProduct.price).toFixed(2)
                    onChange=this.updatePrice
                  />
                  <p>
                    Price (including VAT):" "
                    Number(listProduct.price * 1.2).toFixed(2) €
                  </p>
                  <br />
                  <input
                    className="btn__update"
                    type="submit"
                    value="Update product"
                  />
                </form>
              </div>
              <div className="category__align--desk">
                <h2 className="product__title">Category</h2>
                <p className="product__category">listProduct.category</p>
              </div>
            </div>
          </div>
        );
      
    
    

【讨论】:

哦,非常感谢!!这正是我想要制作的! @RitalCharmant 抱歉,我应该在更新状态时指出输入值之间的类型差异,它将是字符串与数字,与产品 ID 类似,它是产品中的数字对象,但 id 路由参数将是一个字符串。这就是为什么有时ide.target.value 通过Number 函数运行,因此值类型是一致的。 非常感谢!我有最后一个问题,我怎么知道应该这样做?是不是写在什么地方?因为我试图寻找信息但我没有找到 @RitalCharmant 这是个好问题。我确实链接到有关提升状态的官方文档。通常,您希望尽可能地限制变量的范围,找到将状态/等向下推入层次结构以进一步限制范围和组件将其提升到壁橱共同祖先之间的点,以便所有需要它的组件都可以访问它.除了提升状态,您还可以使用 React Context API。除此之外,您还会发现像 Redux/Mobx 这样的全局状态管理库,其中组件“订阅”应用程序“状态”。 非常感谢您的解释以及您花时间解释自己,我现在理解得更好了,即使我仍然需要努力才能完全同化。您能向我解释一下我无法理解的这行代码吗?我知道这是一个三元运算符,但三个点是干什么用的? const updatePrice = (id, price) =&gt; setProductsData((productsData) =&gt; productsData.map((product) =&gt; product.id === Number(id) ? ...product,price: Number(price): product));;

以上是关于React 中来自 API 的状态的主要内容,如果未能解决你的问题,请参考以下文章

当api通过reducer来自状态时如何使用axios获取?

如何使用 React Hooks 和 Context API 正确地将来自 useEffect 内部调用的多个端点的数据添加到状态对象?

React componentDidMount() 初始化状态

如何在 React 中使用 fetch() API 来设置状态

React Hooks:如何在 useEffect 中设置状态?

每秒更新来自 api 的消息