多分派Redux Thunk或setState有任何问题,会阻止组件更新

xdyibdwo  于 2023-11-19  发布在  其他
关注(0)|答案(1)|浏览(103)

我得到这个奇怪的错误,警告:无法更新一个组件(Connect(Cart)),而渲染一个不同的组件(Cart).要找到坏的setState()调用内Cart .我不知道哪里是真正的问题,在Cart.js或cartActions.js?cartActions. js使用Redux Thunk,有时多dispacth在一个动作创建者.提前感谢.
Cart.js,

import { Component, Fragment } from "react";
import AppNavbar from './AppNavbar';
import { Card, CardText, CardBody, CardTitle, CardSubtitle, Button, Alert, Container } from 'reactstrap';
import PropTypes from 'prop-types';
import { connect }from 'react-redux';
import { getCart, deleteFromCart } from '../actions/cartActions';
import Checkout from "./Checkout";
import { checkout } from '../actions/orderActions';

class Cart extends Component {
    /*state = {
        loaded: false
    }*/

    constructor(props) {
        super(props);
        this.state = {
            loaded: false
        }
    }

    //this.state.loaded = false;

    static propTypes = {
        getCart: PropTypes.func.isRequired,
        isAuthenticated: PropTypes.bool,
        addToCart: PropTypes.func.isRequired,
        deleteFromCart: PropTypes.func.isRequired,
        user: PropTypes.object.isRequired,
        cart: PropTypes.object.isRequired,
        checkout: PropTypes.func.isRequired
    }

    getCartItems = async (id) => {
        await this.props.getCart(id);
        //this.state.loaded = true;
        this.setState({
            loaded:  true
        });
    }

    onDeleteFromCart = (id, itemId) => {
        this.props.deleteFromCart(id, itemId);
    }

    render() {
        const user = this.props.user;
        if (this.props.isAuthenticated && !this.props.cart.loading && !this.state.loaded) {
            this.getCartItems(user._id);
        }

        //Why bind this with multiple things below?
        //<Button color="danger" onClick={this.onDeleteFromCart.bind(this, user._id, item.productId)}>Delete</Button>
        return (
            <div>
                <AppNavbar />
                {this.props.isAuthenticated ?
                    <Fragment>
                        {this.props.cart.cart ? null :
                            <Alert color="info" className="text-center">Your cart is empty!</Alert>
                        }
                    </Fragment>
                    : <Alert color="danger" className="text-center">Login to View!</Alert>
                }
                {this.props.isAuthenticated && !this.props.cart.loading && this.state.loaded && this.props.cart.cart ?
                    <Container>
                        <div className="row">
                            {this.props.cart.cart.items.map(item => (
                                <div className="col-md-4">
                                    <Card>
                                        <CardBody>
                                            <CardTitle tag="h5">{item.name}</CardTitle>
                                            <CardSubtitle tag="h6">USD {item.price}</CardSubtitle>
                                            <CardText>Quantity - {item.quantity}</CardText>
                                            <Button color="danger" onClick={this.onDeleteFromCart.bind(this, user._id, item.productId)}>Delete</Button>
                                        </CardBody>
                                    </Card>
                                    <br />
                                </div>
                            ))}
                        <div class="col-md-12">
                            <Card>
                                <CardBody>
                                    <CardTitle  tag="h5">Total Cost = USD {this.props.cart.cart.bill}</CardTitle>
                                    <Checkout 
                                        user={user._id} 
                                        amount={this.props.cart.cart.bill}
                                        checkout={this.props.checkout}
                                    />
                                </CardBody>
                            </Card>
                        </div>
                        </div>
                    </Container>
                    : null}
            </div>
        );
    }
}

const mapStateToProps = state => ({
    cart: state.cart,
    isAuthenticated: state.auth.isAuthenticated,
    user: state.auth.user
})

export default connect(mapStateToProps, {getCart, deleteFromCart, checkout})(Cart);

字符串
carts.js,

//import axios from "axios";
import axios from "../http-common";
import { GET_CART, ADD_TO_CART, DELETE_FROM_CART, CART_LOADING } from "./types";
import { returnErrors } from './errorActions';

export const getCart = (id) => (dispatch) => {
    dispatch(setCartLoading());
    axios.get(`/api/cart/${id}`)
        .then(res => {
            dispatch({
                type: GET_CART,
                payload: res.data
            });
        })
        .catch(err => {
            dispatch(returnErrors(err.response.data, err.response.status));
        });
}

export const addToCart = (id, productId, quantity) => (dispatch) => {
    axios.post(`/api/cart/${id}`, {productId, quantity})
    .then(res => {
        dispatch({
            type: ADD_TO_CART,
            payload: res.data
        });
    })
    .catch(err => {
        dispatch(returnErrors(err.response.data, err.response.status));
    });
}

//My addition for testing below,
/*export const updateCart = (id) => (dispatch) => {
    axios.put(`/api/cart/${id}`)
    .then(res => {
        dispatch({
            type: UPDATE_CART,
            payload: Promise.all([id, res.data])
        });
    })
    .catch(err => {
        dispatch(returnErrors(err.response.data, err.response.status));
    });
}*/

export const deleteFromCart = (userId, itemId) => (dispatch) => {
    axios.delete(`/api/cart/${userId}/${itemId}`)
    .then(res => {
        dispatch({
            type: DELETE_FROM_CART,
            payload: res.data
        });
    })
    .catch(err => {
        dispatch(returnErrors(err.response.data, err.response.status));
    });
}

export const setCartLoading = () => {
    return {
        type: CART_LOADING
    }
}

ttisahbt

ttisahbt1#

问题

代码正在调度getCart操作,并在render方法中排队状态更新。两者都是***无意的***副作用。

class Cart extends Component {
  ...

  getCartItems = async (id) => {
    await this.props.getCart(id);    // <-- unintentional side-effect
    this.setState({ loaded: true }); // <-- unintentional side-effect
  }

  render() {
    const user = this.props.user;
    if (this.props.isAuthenticated &&
      !this.props.cart.loading &&
      !this.state.loaded
    ) {
      this.getCartItems(user._id);
    }

    return (
      ...
    );
  }
}

const mapStateToProps = state => ({
  cart: state.cart,
  isAuthenticated: state.auth.isAuthenticated,
  user: state.auth.user
});

const mapDispatchToProps = { getCart, deleteFromCart, checkout }

export default connect(mapStateToProps, mapDispatchToProps)(Cart);

字符串

解决方案

React渲染函数被认为是 * 纯同步 * 函数。使用componentDidMountcomponentDidUpdate生命周期方法将这些作为 * 故意的 * 副作用发出。
范例:

class Cart extends Component {
  ...

  getCartItems = async () => {
    const { cart, getCart, isAuthenticated, user } = this.props;
    const { loaded } = this.state;

    if (isAuthenticated && !cart.loading && !loaded) {
      await getCart(user.id);
      this.setState({ loaded: true });
    }
  }

  componentDidMount() {
    this.getCartItems();
  }

  componentDidUpdate() {
    this.getCartItems();
  }

  render() {
    // no unintentional side-effects :)

    return (
      ...
    );
  }
}

const mapStateToProps = state => ({
  cart: state.cart,
  isAuthenticated: state.auth.isAuthenticated,
  user: state.auth.user
});

const mapDispatchToProps = { getCart, deleteFromCart, checkout }

export default connect(mapStateToProps, mapDispatchToProps)(Cart);

相关问题