【问题标题】:Component stopped to re-render after using privateroute使用 privateroute 后组件停止重新渲染
【发布时间】:2019-06-13 07:53:05
【问题描述】:

我正在 Reactjs(简单商店)中制作第一个应用程序,并且在更改状态值后重新渲染 UI 时遇到了一些问题。在 App.js 中使用 PrivateRoute 之前一切正常。现在,当我单击“添加到卡”时,产品状态会发生应有的变化(数量设置为 1),但在 UI 上它仍然为 0(因此不会重新渲染组件)。 也许是因为我在 App.js 路由中使用 component={ProductList} 而不是 render={() => } 。但是渲染不再适用于 PrivateRoute(抛出错误)。我不确定我做错了什么,但我很伤心,这是我在 reactjs 中写任何东西的第一天,所以我需要一点帮助。

App.js

import React from 'react';
import './../App.css';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Navbar from '../components/navbar';
import ProductList from '../components/productList';
import Checkout from '../components/checkout';
import Payment from '../components/payment';
import Footer from '../components/footer';
import SignIn from '../components/auth/signIn';
import SignUp from '../components/auth/signUp';
import { PrivateRoute } from '../components/auth/privateRoute';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loggedIn: true,
      selectedProducts: [],
      products: [
        {
          "id": 1,
          "name": "11 bit studios SA",
          "grossPrice": 390.00,
          "netPrice": 360.00,
          "image": "https://jarock.pl/upload/posts/836323184026f846637c9e455b3950a4.jpg",
          "description": "",
          "quantity": 0
        },
        {
          "id": 2,
          "name": "PEKAO",
          "grossPrice": 108.00,
          "netPrice": 100.00,
          "image": "https://pbs.twimg.com/profile_images/1107386368609652736/U91cV_vU.png",
          "description": "",
          "quantity": 0
        },
        {
          "id": 3,
          "name": "CDPROJEKT",
          "grossPrice": 198.00,
          "netPrice": 170.00,
          "image": "https://yt3.ggpht.com/a/AGF-l7_JugJ8uDvDTXqsBPLuT4ZueARyKoM1dVG1gA=s900-mo-c-c0xffffffff-rj-k-no",
          "description": "",
          "quantity": 0
        },
        {
          "id": 4,
          "name": "CCC",
          "grossPrice": 147.00,
          "netPrice": 135.00,
          "image": "https://ccc.eu/start_page/assets/img/logo.jpg",
          "description": "",
          "quantity": 0
        }
      ]
    }

    this.AddItemToSelectedProductList = this.AddItemToSelectedProductList.bind(this);
    this.RemoveItemFromSelectedProductList = this.RemoveItemFromSelectedProductList.bind(this);
  }

  AddItemToSelectedProductList(newProduct) {
    var newProductList = this.state.selectedProducts;
    const existingProduct = newProductList.find(item => newProduct.id === item.id);

    if (existingProduct) {
      existingProduct.quantity++;

      this.setState((state) => ({
        products: state.products
      }));
    }
    else {
      newProduct.quantity = 1;
      newProductList.push(newProduct);
      this.setState({ selectedProducts: newProductList });
    }
  };


  RemoveItemFromSelectedProductList(productToRemove) {
    var newProductList = this.state.selectedProducts;
    if (newProductList.length > 0) {
      const productToRemoveIndex = newProductList.indexOf(productToRemove);
      newProductList.splice(productToRemoveIndex, 1);
      this.setState({ selectedProducts: newProductList });
    }

    var displayedProductList = this.state.products;
    const displayedProduct = displayedProductList.find(x => x.id === productToRemove.id);
    displayedProduct.quantity = 0;
    this.setState({ products: displayedProductList });
  };

  render() {
    return (
      <Router>
        <div className="main-div">
          <Navbar checkoutItems={this.state.selectedProducts} />
          <PrivateRoute exact path='/' component={ProductList} products={this.state.products} selectProductHandler={this.AddItemToSelectedProductList} />
          <PrivateRoute path="/checkout" component={Checkout} selectedProducts={this.state.selectedProducts} removeProductHandler={this.RemoveItemFromSelectedProductList} />
          <PrivateRoute path="/payment" component={Payment} />
          <Route path="/signin" component={SignIn} />
          <Route path="/signup" component={SignUp} />
        </div>
        <Footer />
      </Router>
    );
  }
}

export default App;

PrivateRoute.js

import React from 'react';
import { Route, Redirect } from 'react-router-dom';

export const PrivateRoute = ({ component, redirectTo, ...rest }) => {
  return (
    <Route {...rest} render={routeProps => {
      return localStorage.getItem('user') ? (
        renderMergedProps(component, routeProps, rest)
      ) : (
          <Redirect to={{
            pathname: '/signin',
            state: { from: routeProps.location }
          }} />
        );
    }} />
  );
};

const renderMergedProps = (component, ...rest) => {
  const finalProps = Object.assign({}, ...rest);
  return (
    React.createElement(component, finalProps)
  );
}

export const PropsRoute = ({ component, ...rest }) => {
  return (
    <Route {...rest} render={routeProps => {
      return renderMergedProps(component, routeProps, rest);
    }} />
  );
}

之前(当一切正常时)我没有在 App.js 中使用 PrivateRoute。我的产品列表的路径如下所示:

<Route exact path="/" render={() => <ProductList products={this.state.products} selectProductHandler={this.AddItemToSelectedProductList} />} />

在@Sky 提出建议后,我更改了“添加到卡片”的实现。我使用 immerjs 来简化它。在所有更改之后一切都像以前一样工作,UI 不会重新渲染。这是更改的代码:

  AddItemToSelectedProductList(newProduct) {
    this.AddToSelectedProducts(newProduct);
    this.AddToProducts(newProduct);
  };

  AddToProducts(newProduct) {
    const nextState = produce(this.state.products, draft => {
      const productIndex = draft.findIndex(item => item.id === newProduct.id);
      draft[productIndex].quantity = draft[productIndex].quantity + 1;
    });
    this.setState({ products: nextState });
  }

  AddToSelectedProducts(newProduct) {
    var newProductList = this.state.selectedProducts;
    const existingProduct = newProductList.find(item => newProduct.id === item.id);
    var nextState = [];

    if (existingProduct) {
      nextState = produce(newProductList, draft => {
        const existingProductIndex = draft.findIndex(item => item.id === newProduct.id);
        draft[existingProductIndex].quantity = draft[existingProductIndex].quantity + 1;
      });
    }
    else {
      const nextProductState = produce(newProduct, draft => {
        draft.quantity = 1;
      });
      nextState = produce(newProductList, draft => {
        draft.push(nextProductState);
      });
    }

    this.setState({ selectedProducts: nextState });
  };

【问题讨论】:

    标签: reactjs react-router-dom


    【解决方案1】:

    在 React 中,不允许对象突变,每次都必须创建一个新的对象副本。

    existingProduct.quantity++;
    

    应该是

    const newProduct = {...existingProduct, quantity: existingProduct.quantity + 1}
    
    this.setState((state) => ({
      products: state.products
    }));
    

    这里有类似的问题,你应该创建一个数组的新实例。

    【讨论】:

    • 我已经更改了“添加到购物篮”功能的实现,并使用了 immerjs 来实现。但仍然没有重新渲染 UI。当我查看状态值时,它们是正确的。
    【解决方案2】:

    修改 AddToProducts 这样的 UI 后再次正确渲染:

    AddToProducts(newProduct) {
        var newProductList = this.state.products.slice();
        const productIndex = newProductList.findIndex(item => item.id === newProduct.id);
        newProductList[productIndex].quantity = newProductList[productIndex].quantity + 1;
    
        this.setState({ products: newProductList });
    }
    

    我不确定为什么这不起作用(immerjs):

    AddToProducts(newProduct) {
       const nextState = produce(this.state.products, draft => {
          const productIndex = draft.findIndex(item => item.id === newProduct.id);
          draft[productIndex].quantity = draft[productIndex].quantity + 1;
        });
       this.setState({ products: nextState });
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-07
      • 2019-07-27
      • 2011-01-20
      • 1970-01-01
      • 1970-01-01
      • 2021-04-30
      • 2021-11-02
      • 2020-12-03
      相关资源
      最近更新 更多