React router - 需要单击LINK两次才能将props传递给Component

时间:2017-05-05 21:45:55

标签: reactjs components render router

我正在使用React Router 4.

我有一个ShopCoffee组件,允许将项目添加到购物车。

点击购物车LINK React渲染购物车组件。

没有路由器(当购物车组件与主应用程序位于同一页面时),购物车工作正常。

但是另一个Cart组件(连接到路由器)没有收到道具,因此Cart呈现为空。 enter image description here

如果我再次点击LINK to Cart(购物车图标),它会重新呈现并显示所有项目。

enter image description here

所以,如果我像这样渲染组件:

<Cart itemsInCart = {this.state.itemsInCart} deleteItemFromCart = {this.deleteItemFromCart} />

它可以正常工作,

但是当我这样做时:

const CartRoute = (props) => (<Cart itemsInCart = {this.state.itemsInCart} deleteItemFromCart = {this.deleteItemFromCart} {...props} />);

仅当我在LINK标签上点击两次时才有效。

以下是代码:

app.jsx

import React from "react";
import ReactDOM from "react-dom";

import Main from "./components/main.component.jsx";
import { createStore } from "redux";
import { Provider } from "react-redux";
import { BrowserRouter, Route } from "react-router-dom";

var app = document.getElementById("app");

function mainAppReducer(state, action) {
    if (!state) return {
        items: []
    }
    switch (action.type) {
        case 'ADD_TO_CART' : console.log('ADD_TO_CART');
                    console.log("action.items == ", action.items);
                    console.log("state==",state);
                    return Object.assign({}, action.items); // state уже содержит все данные об объектах в корзине, поэтому ничего не добавляем

        case 'DELETE_FROM_CART' : console.log('DELETE_FROM_CART');
                    console.log("action.items == ", action.items);
                    console.log("state==",state);
                    return Object.assign({}, action.items); // state уже содержит все данные об объектах в корзине, поэтому ничего не добавляем     
    }
}
const store = createStore(mainAppReducer);
var render = () => ReactDOM.render(
                            <BrowserRouter>
                                <Provider store={store}>
                                    <Route path="/" component = {Main} />
                                </Provider>
                            </BrowserRouter>
                        , app);
store.subscribe(render);
render();

main.component.jsx

import React from "react";
import Header from "./header.component.jsx";
import Footer from "./footer.component.jsx";
import Cart from "./cart.component.jsx";
import Checkout from "./checkout.component.jsx";
import ShopCoffee from "./shop-coffee.component.jsx";
import Rent from "./rent.component.jsx";
import Repair from "./repair.component.jsx";
import Contacts from "./contacts.component.jsx";
import ToCartButton from "./to-cart-button.component.jsx";
import { connect } from "react-redux";
import { Route, Switch } from "react-router-dom";

export class Main extends React.Component {
    constructor(props) {
        super(props);
        this.addNewItemToCart = this.addNewItemToCart.bind(this);
        this.deleteItemFromCart = this.deleteItemFromCart.bind(this);
        this.state = {itemsInCart : []};
    }
    addNewItemToCart(itemsInCart) {
        this.props.dispatch({type : 'ADD_TO_CART',
                             items: itemsInCart});
        this.setState({itemsInCart : itemsInCart});
        console.log("this.state", this.state);
    }

    deleteItemFromCart(i) {
        var itemToDelete = this.state.itemsInCart[i];
        console.log("itemToDelete == ", itemToDelete);
        var itemsLeft = this.state.itemsInCart.filter((x,ind) => ind != i);

        this.props.dispatch({type : 'DELETE_FROM_CART',
                             items: itemsLeft});        
        console.log("itemsLeft == ", itemsLeft);
        this.setState({itemsInCart: itemsLeft});

    }

    getItemsInCart(itemsInCart) {
        return itemsInCart;
    }

    render() {
        const ShopCoffeeRoute = (props) => (<ShopCoffee itemsInCart = {this.state.itemsInCart} addNewItemToCart = {this.addNewItemToCart} {...props} />);
        const CartRoute = (props) => (<Cart itemsInCart = {this.state.itemsInCart} deleteItemFromCart = {this.deleteItemFromCart} {...props} />);
        const CheckoutRoute = (props) => (<Checkout itemsInCart = {this.state.itemsInCart} {...props} />);
        return (
            <main>
                <Header />
                <Switch>
                    <Route exact path="/" render={ShopCoffeeRoute} />
                    <Route path="/rent" component={Rent} />
                    <Route path="/repair" component={Repair} />
                    <Route path="/contacts" component={Contacts} />             
                    <Route path="/checkout" render={CheckoutRoute} />
                    <Route path="/cart" render={CartRoute} />
                </Switch>
                <Cart itemsInCart = {this.state.itemsInCart} deleteItemFromCart = {this.deleteItemFromCart} />
                <ToCartButton itemsInCart = {this.state.itemsInCart} />
                <Footer />
            </main>
        );
    }
}

export default connect((store) => store)(Main);

to-cart-button.component.jsx

import React from 'react';
import { Link } from "react-router-dom";

export default class ToCartButton extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <Link to="/cart">
            <section className="to-cart-button">
                <div className="to-cart-button__text-container">
                    <p className="to-cart-button__text-container__text">
                        {this.props.itemsInCart.length}
                    </p>
                </div>
            </section>
            </Link>
        );
    }
}

cart.component.jsx

import React from "react";
import { Link } from "react-router-dom";

export default class Cart extends React.Component {
    constructor(props) {
        super(props);
        this.state = {itemsInCart : []};
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.itemsInCart != this.state.itemsInCart) {
            this.setState({itemsInCart : nextProps.itemsInCart});
        }
    }

    deleteItemFromCart(i) {
        var itemsLeft = this.state.itemsInCart.filter((x,ind) => ind != i);
        this.props.deleteItemFromCart(i);
        console.log("itemsLeft === ", itemsLeft);
        this.setState({itemsInCart : itemsLeft});
    }

    render() {
        console.log("Cart /");
        console.log("this.props == ",this.props);
        var imgPath = "img/coffee/"
        var itemsInTable;

        var itemsInCart = this.state.itemsInCart;
        var totalPrice = 0;

        if (!itemsInCart.length) {
            itemsInTable =  (<tr>
                                <td colSpan="5">Ваша корзина пуста</td>
                            </tr>);
        } 
        else {
            totalPrice = 0;
            itemsInTable = (itemsInCart.map((item, i) => {
                                totalPrice += +item.price;
                                console.log("totalPrice==",totalPrice);
                                return (
                                    <tr key={i}>
                                        <td>{item.title}</td>
                                        <td><img src={imgPath + item.image} /></td>
                                        <td>1 шт.</td>
                                        <td>{item.price} руб.</td>
                                        <td><button className="cart__table__delete-button" onClick={this.deleteItemFromCart.bind(this, i)}><i className="fa fa-times"></i></button></td>
                                    </tr>);
                }));
        }

        return (
            <section className="cart">
                <div className="container">
                    <div className="row">
                        <div className="cart__title-container">
                            <h2 className="cart__title-container__title">
                                Ваша корзина
                            </h2>
                        </div>
                    </div>
                    <div className="row">
                        <div className="col-md-12">
                            <table className="cart__table">

                                <tbody>
                                    <tr>
                                        <th colSpan="5">Список товаров</th>
                                    </tr>                           
                                    {itemsInTable}  
                                    <tr>
                                        <td></td>
                                        <td></td>
                                        <td>Итого:</td>
                                        <td>{totalPrice} руб.</td>
                                        <td></td>
                                    </tr>                                                                   
                                </tbody>
                            </table>
                            <div className="cart__next-button-container">
                                <Link to="/checkout"><button className="cart__next-button">Далее >></button></Link>
                            </div>
                        </div>              
                    </div>
                </div>
            </section>
        );
    }
}

2 个答案:

答案 0 :(得分:1)

答案 1 :(得分:0)

我想扩大alexfrize的回应。他引用了一个帖子指出:

  

在安装过程中,React不会使用初始道具调用componentWillReceiveProps。仅当组件的某些道具可能更新时,才调用此方法。在已安装的组件接收新道具之前,将调用componentWillReceiveProps()。

以上是什么意思?

如果您的代码依赖于componentWillReceiveProps()来执行某项操作(即设置状态),则在使用React Router时,该方法不会在第一次单击Link时触发。为了解决这个问题,您应该调用componentWillReceiveProps()内部componentDidMount()中的相同(或相似)代码。如有必要,请在componentDidMount()中包含一些代码,以确保该代码具有抗错误性。