簡體   English   中英

修改狀態后組件未正確重新渲染

[英]Component is not re-rendered properly after state is modified

在我添加或更新產品后, listcomponent應該重新渲染,它確實如此,但是使用 array.map 生成的部分沒有更新任何我在這里缺少的東西,這是我的減速器

import produce from "immer"

import {
  PRODUCT_REMOVED,
  PRODUCT_ADDED,
  PRODUCT_UPDATED,
  ADD_ERROR,
  PRODUCT_LIST,

  LIST_ERROR
} from "../actions/types";

// define default redux state
const DEFAULT_STATE = {
  products: [],
  errorMSG: ""
};
/**
 * return new state with new product subtree based on action type
 * @param  {Object} state  redux old state
 * @param  {Object} action action performed
 * @return {Object}        redux new state
 */
export default (state = DEFAULT_STATE, action) => {
  let new_products;
  let i;
  let j;
  // return new state with new product subtree based on action type
  switch (action.type) {
    case PRODUCT_ADDED:

console.log(PRODUCT_ADDED)
      // new_products = state.products
      // console.log('new_products',new_products)
      // console.log('new_products[0]',new_products[0])
      // new_products.unshift(action.payload);
      // console.log('new_products',new_products)
      // console.log('new_products[0]',new_products[0])
      return produce(state, draftState => {
        draftState.products.unshift(action.payload)
        draftState.errorMSG = ""
      })

    case PRODUCT_UPDATED:
      //const newProducts = [...state.products]
      //let modifiedProductIdx = newProducts.findIndex(({_id}) => _id == action.payload._id)
      //newProducts.splice(modifiedProductIdx,1,action.payload)   
      // new_products = state.products;
      // for (i = 0; i < new_products.length; i++)
      //   if (new_products[i]._id === action.payload._id) {
      //     new_products[i] = action.payload;
      //     break;
      //   }
      return produce(state, draftState => {
        new_products = state.products
        for (i = 0; i < new_products.length; i++)
          if (new_products[i]._id === action.payload._id) {
            new_products[i] = action.payload;
            break;
          }
        draftState.errorMSG = ""
        draftState.products = new_products
      })

    case PRODUCT_REMOVED:
      // new_products = state.products;
      // for (i = 0; i < new_products.length; i++)
      //   if (new_products[i]._id === action.payload._id) {
      //     new_products.splice(i, 1);
      //     break;
      //   }
      return produce(state, draftState => {
        new_products = state.products;
        for (i = 0; i < new_products.length; i++)
          if (new_products[i]._id === action.payload._id) {
            new_products.splice(i, 1);
            break;
          }
        draftState.errorMSG = ""
        draftState.products = new_products
      })
    case ADD_ERROR:
      // return {
      //   ...state,
      //   errorMSG: action.payload
      // };
      return produce(state, draftState => {
        draftState.errorMSG = action.payload
      })
    case PRODUCT_LIST:

      // return {
      //   ...state,
      //   products: action.payload,
      //   errorMSG: ""
      // };
      return produce(state, draftState => {
        draftState.errorMSG = ""
        draftState.products = action.payload
      })
    case LIST_ERROR:
      // return {
      //   ...state,
      //   errorMSG: action.payload
      // };
      return produce(state, draftState => {
        draftState.errorMSG = action.payload
      })

    default:
      // return state
      return produce(state, draftState => {
        draftState.errorMSG = action.payload
      })
  }
};```

和我的 rootreducer

import { combineReducers } from "redux";

import authReducer from "./auth";
import subjectReducer from "./subject";
import gradeReducer from "./grade"
import studentReducer from "./student"
import productReducer from "./product"
import teacherReducer from "./teacher"
//create root reducer that will contail other reducers
const rootReducer = combineReducers({
    auth: authReducer,
    subject: subjectReducer,
    grade: gradeReducer,
    student: studentReducer,
    teacher:teacherReducer,
    product:productReducer,

});

export default rootReducer;

和我的組件

import React from 'react';

import { connect } from "react-redux";
import { compose } from "redux";
import { withStyles } from "@material-ui/core/styles";
import PropTypes from "prop-types";
import { Redirect } from "react-router-dom";

import { validate_name, validate_num } from "../../utils/validators";
import CardActionArea from '@material-ui/core/CardActionArea';
import CardActions from '@material-ui/core/CardActions';
import AddProduct from "./AddProduct";
import Spinner from "../../Spinner";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import Button from '@material-ui/core/Button';

import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
//import AddSubject from './AddSubject'
import * as actions from "../../actions/ProductActions";

import Product from './Product'

const styles = theme => ({
  root: {
    flexGrow: 1,
  },
  paper: {
    padding: theme.spacing(2),
    textAlign: 'center',
    color: theme.palette.text.secondary,
  }
});
class ListProduct extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      is_loading: false,
      open: false,
      err: '',
      openDelete: false
    }
  }
  static getDerivedStateFromProps(props, state) {
    console.log('getDerivedStateFromProps',props.product.products[0].name)

    return {
      products: props.product.products ,
      is_loading: false,
      open: false,
      err: '',
      openDelete: false};
  }
  componentDidUpdate() {
    console.log('componentDidUpdate')
  }
  async componentDidMount() {
    console.log('componentDidMount')

    //await this.props.listProduct();
    // let { errorMSG, products } = this.props.product;
    // if (errorMSG) {
    //   this.setState({ err: errorMSG });
    // } else this.setState({ err: "", products: products });
  }
  /**
   * handle edit after add button is clicked
   * @param  {Object} e  click event
   */
  async handleSubmit(e) {
    e.preventDefault();
    let name_isValid = await validate_name('Name', this.state.name);
    let description_isValid = await validate_name("Description ", this.state.description);
    let price_isValid = await validate_num("Price", this.state.price);
    let stock_isValid = await validate_num("Stock", this.state.stock);


    if ((name_isValid.status && description_isValid.status && price_isValid.status && stock_isValid.status) === true) {
      // call product actions creator
      await this.props.editProduct({
        product_id: this.state.id,
        name: this.state.name,
        description: this.state.description,
        price: this.state.price,
        stock: this.state.stock,


      });
      let { errorMSG } = this.props.product;
      if (errorMSG) {
        this.setState({ err: errorMSG });
      } else {
        this.setState({ err: "", open: false });
      }
    } else {
      if (name_isValid.status === false)
        this.setState({ err: name_isValid['text'] });
      else if (description_isValid.status === false)
        this.setState({ err: description_isValid['text'] });
      else if (price_isValid.status === false)
        this.setState({ err: price_isValid['text'] });
      else if (stock_isValid.status === false)
        this.setState({ err: stock_isValid['text'] });

    }
  }

  /**
   * handle change in inputs
   * @param  {String} name input label
   */
  handleChange = name => event => {
    this.setState({
      [name]: event.target.value
    });
  };
  /**
   * close/open dialog box
   */
  toggleDialog = () => {
    this.setState({ open: !this.state.open });
  };
  /**
   * handle edit after enter key is pressed
   * @param  {Object} event  key press event
   */
  handleSubmitWithEnterKey = async event => {
    if (event.key === "Enter") {
      event.preventDefault();
      let name_isValid = await validate_name('Name', this.state.name);
      let description_isValid = await validate_name("Description ", this.state.description);
      let price_isValid = await validate_num("Price", this.state.price);
      let stock_isValid = await validate_num("Stock", this.state.stock);


      if ((name_isValid.status && description_isValid.status && price_isValid.status && stock_isValid.status) === true) {
        // call product actions creator
        await this.props.editProduct({
          product_id: this.state.id,

          name: this.state.name,
          description: this.state.description,
          price: this.state.price,
          stock: this.state.stock,


        });
        let { errorMSG, products } = this.props.product;
        if (errorMSG) {
          this.setState({ err: errorMSG });
        } else {
          this.setState({ err: "", open: false });

        }
      } else {
        if (name_isValid.status === false)
          this.setState({ err: name_isValid['text'] });
        else if (description_isValid.status === false)
          this.setState({ err: description_isValid['text'] });
        else if (price_isValid.status === false)
          this.setState({ err: price_isValid['text'] });
        else if (stock_isValid.status === false)
          this.setState({ err: stock_isValid['text'] });

      }
    }
  };
  /**
  * toggle edit dialog
  * @param  {String} id   product id
  * @param  {String} name product name
 * @param  {Number} price   product price
  * @param  {Number} stock   product stock
  * @param  {String} description product description

  */
  toggleEditDialog = (id=false, name=false, price=false, stock=false, description) => e => {
    this.setState({
      open: !this.state.open,
      id: id,
      name: name,
      price: price,
      stock: stock,
      description: description,

      err: ""
    });
  };

  /**
   * handle confirm delete
   * @param  {Object} e button click even
   */
  async handleDelete(e) {
    e.preventDefault();
    // call product actions creator
    await this.props.removeProduct({
      product_id: this.state.id
    });
    let { errorMSG, products } = this.props.product;
    if (errorMSG) {
      this.setState({ err: errorMSG });

    } else this.setState({ openDelete: false, err: "", products: products });
  }
  /**
   * toggle  delete dialog
   * @param  {String}   id         product id
   */
  toggleDeleteDialog = id => e => {
    this.setState({ openDelete: !this.state.openDelete, id: id, err: "" });
  };


  /**
 * buy a product
 * @param  {String}   id         product id
 */
  buy = id => async e => {
    // call product actions creator
    await this.props.buyProduct({
      product_id: this.state.id
    });
    let { errorMSG, products } = this.props.product;
    if (errorMSG) {
      this.setState({ err: errorMSG });
    } else {
      this.setState({ err: "" });

    }
  };
  /**
   * handle change in inputs
   * @param  {String} name input label
   */
  handleChange = name => event => {
    this.setState({
      [name]: event.target.value
    });
  };

  render() {

    if (this.props.pub !== true) {
      if (this.props.isAuthenticated !== true) {
        return <Redirect to="/login" />;
      }
    }

    const { classes } = this.props
    let  products  = this.state.products
    products = (typeof products === 'undefined') ? [] : products
console.log('rerender',this.props.product.products[0].name)

    return (
      <div className={classes.root} data-test="ListProductComponent">
        <AddProduct />
        <h1>props : {this.props.product.products[0].name}</h1>
        <h1>state : {this.state.products[0].name}</h1>
        <h1>products : {products[0].name}</h1>

        <br />
        <Grid container spacing={3}>
          {this.state.products
            .map(product => (
              <Grid item xs={12} sm={4} lg={3}>
                <Product currentProduct={product} />
                <CardActions>
                  <Button onClick={this.buy(product._id)} size="small" color="primary">
                    Buy
        </Button>
                  <Button onClick={this.toggleEditDialog(product._id, product.name, product.price, product.stock, product.description)} size="small" color="primary">
                    Edit
        </Button>
                  <Button onClick={this.toggleDeleteDialog(product._id)} size="small" color="secondary">
                    Delete
        </Button>

                  {product.price + ' KSH'}
                </CardActions>
              </Grid>))}
        </Grid>
        <Dialog
          data-test="EditDialog"
          open={this.state.open}
          onClose={this.toggleEditDialog()}
          aria-labelledby="form-dialog-title"
        >
          <DialogTitle id="form-dialog-title">Edit Product</DialogTitle>
          <DialogContent>
            <TextField
              id="name"
              label="Name"
              onChange={this.handleChange("name")}
              margin="normal"
              value={this.state.name}
              autoFocus={true}
              onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
            />
            <br />
            <TextField
              id="description"
              label="Description"
              onChange={this.handleChange("description")}
              margin="normal"
              value={this.state.description}
              onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
            />
            <br />
            <TextField
              id="price"
              label="Price"
              onChange={this.handleChange("price")}
              margin="normal"
              value={this.state.price}
              onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
            />
            <TextField
              id="stock"
              label="Stock"
              onChange={this.handleChange("stock")}
              margin="normal"
              value={this.state.stock}
              onKeyDown={this.handleSubmitWithEnterKey.bind(this)}
            />
            <h1>{this.state.err}</h1>
          </DialogContent>
          <DialogActions>
            <Button
              onClick={this.toggleEditDialog()}
              color="primary"
            >
              Cancel
            </Button>
            <Button onClick={this.handleSubmit.bind(this)} color="primary">
              Edit
            </Button>
          </DialogActions>
        </Dialog>
        <Dialog
          data-test="DeleteDialog"
          open={this.state.openDelete}
          onClose={this.toggleDeleteDialog("")}
          aria-labelledby="form-dialog-title"
        >
          <DialogTitle id="form-dialog-title">Delete Product</DialogTitle>

          <DialogActions>
            <Button onClick={this.toggleDeleteDialog("")} color="primary">
              Cancel
            </Button>
            <Button color="secondary" onClick={this.handleDelete.bind(this)}>
              Confirm Delete
            </Button>
          </DialogActions>
        </Dialog>
      </div>
    );
  }
}
ListProduct.propTypes = {
  classes: PropTypes.object.isRequired,
  isAuthenticated: PropTypes.bool.isRequired
};
/**
 * map redux this.state to component props
 * @param  {Object } this.state redux this.state
 * @return {object}        component props
 */
function mapStatetoProps(state) {
  return {
    product: state.product,
    rank: state.auth.rank,
    isAuthenticated: state.auth.isAuthenticated
  };
}

export default compose(
  connect(
    mapStatetoProps,
    actions
  ),
  withStyles(styles)
)(ListProduct);

任何人都可以告訴我我做錯了什么

任何人都可以告訴我我做錯了什么

請任何機構告訴我我做錯了什么 任何機構請告訴我我做錯了什么 任何機構請告訴我我做錯了什么 任何機構請告訴我我做錯了什么 任何機構請告訴我我做錯了什么任何人都可以告訴我我做錯了什么

你不應該直接給state.productsnew_products ,因為它改變了來源。 使用擴展運算符

case PRODUCT_ADDED:
      new_products = ...(state.products);
      new_products.unshift(action.payload);
      return {
        ...state,
        products: new_products,
        errorMSG: ""
      };

避免修改原始數組以獲得所需的結果。 使用擴展運算符嘗試以下解決方案。

case PRODUCT_ADDED:
      return {
        ...state,
        products: [action.payload, ...state.products],
        errorMSG: ""
      };

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM