我正在构建一个简单的Campgrounds CRUD应用程序,以使用MERN堆栈和Redux进行一些练习。

添加一个营地工作正常。添加露营地后,我将路由至露营地列表页面。但是,除非重新加载页面,否则不会检索到新数据。

我认为这与React的生命周期方法有关。

码:

manageCampground.js

import React, { Component } from 'react';
import TextField from '@material-ui/core/TextField';
import Card from '@material-ui/core/Card';
import Button from '@material-ui/core/Button';
import '../../styles/addCampground.css';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {
  actionAddCampground,
  getCampgroundDetails,
  actionUpdateCampground
} from './actions/campgroundActions';

class AddCampground extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: '',
      description: '',
      cost: ''
    };
  }

  componentDidMount() {
    const campground = this.props.campground;
    if (campground._id) {
      this.props.getCampgroundDetails(campground._id);
      this.setState({
        name: campground.name,
        description: campground.description,
        cost: campground.cost
      });
    }
  }

  handleChange = e => {
    const { name, value } = e.target;
    this.setState({
      [name]: value
    });
  };

  addCampground = () => {
    const name = this.state.name;
    const description = this.state.description;
    const cost = this.state.cost;
    this.props.actionAddCampground({
      name,
      description,
      cost
    });
    this.props.history.push('/home');
    console.log('Campground added successfully');
  };

  updateCampground = () => {
    const name = this.state.name;
    const description = this.state.description;
    const cost = this.state.cost;
    this.props.actionUpdateCampground({
      name,
      description,
      cost
    });
    this.props.history.push('/home');
    console.log('Updated successfully');
  };
  render() {
    console.log(this.props);
    return (
      <Card className="add-campground-card">
        <TextField
          name="name"
          className="textfield"
          label="Campground name"
          variant="outlined"
          value={this.state.name}
          onChange={e => this.handleChange(e)}
        />
        <TextField
          name="description"
          className="textfield"
          label="Campground description"
          variant="outlined"
          value={this.state.description}
          onChange={e => this.handleChange(e)}
        />
        <TextField
          name="cost"
          className="textfield"
          type="number"
          label="Campground cost"
          variant="outlined"
          value={this.state.cost}
          onChange={e => this.handleChange(e)}
        />
        {!this.props.campground._id ? (
          <Button
            variant="contained"
            color="primary"
            onClick={this.addCampground}>
            Add Campground
          </Button>
        ) : (
          <Button
            variant="contained"
            color="primary"
            className="update-campground-btn"
            onClick={this.updateCampground}>
            Update Campground
          </Button>
        )}
      </Card>
    );
  }
}

const mapStateToProps = state => {
  return {
    campground: state.campgroundList.singleCampground || ''
  };
};

const mapDispatchToProps = dispatch => {
  return bindActionCreators(
    {
      actionAddCampground,
      getCampgroundDetails,
      actionUpdateCampground
    },
    dispatch
  );
};

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


campgroundList.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getAllCampgrounds } from './actions/campgroundActions';
import Header from '../common/Header';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import { Link } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import '../../styles/landingPage.css';

class CampgroundLanding extends Component {
  componentDidMount() {
    this.props.getAllCampgrounds();
    console.log('From component did mount');
  }


  render() {
    const { campgrounds } = this.props;
    return (
      <>
        <Header />
        {campgrounds.map(campground => (
          <Card className="campground-card" key={campground._id}>
            <CardActionArea>
              <CardContent>
                <Typography gutterBottom variant="h5"
  component="h2">
                  {campground.name}
                </Typography>
                <Typography variant="body2" color="textSecondary"
  component="p">
                  {campground.description}
                </Typography>
              </CardContent>
            </CardActionArea>
            <CardActions>
              <Link
                style={{ textDecoration: 'none', color: 'white' }}
                to={`/campgrounds/${campground._id}`}>
                <Button size="small" color="primary">
                  View Details
                </Button>
              </Link>
              <Button size="small" color="primary">
                Learn More
              </Button>
            </CardActions>
          </Card>
        ))}
        <Link
          style={{ textDecoration: 'none', color: 'white' }}
          to="/campgrounds/add">
          <Button color="primary">Add Campground</Button>
        </Link>
      </>
    );
  }
}

const mapStateToProps = state => {
  return {
    campgrounds: state.campgroundList.campgrounds
  };
};

const mapDispatchToProps = dispatch => {
  return bindActionCreators(
    {
      getAllCampgrounds
    },
    dispatch
  );
};


export default connect(
  mapStateToProps,
  null
)(CampgroundLanding);


campgroundActions.js

import {
  GET_ALL_CAMPGROUNDS,
  ADD_CAMPGROUND,
  GET_CAMPGROUND_DETAILS,
  EDIT_CAMPGROUND
} from '../actionTypes/types';

import axios from 'axios';

const API_URL = `http://localhost:5000/api`;

export const getAllCampgrounds = () => {
  return dispatch => {
    axios
      .get(`${API_URL}/campgrounds`)
      .then(res => {
        dispatch({
          type: GET_ALL_CAMPGROUNDS,
          payload: res
        });
      })
      .catch(err => console.log(err));
  };
};

export const actionAddCampground = campground => {
  return dispatch => {
    axios
      .post(`${API_URL}/campgrounds`, campground)
      .then(res => {
        console.log(res);
        dispatch({
          type: ADD_CAMPGROUND,
          payload: res
        });
      })
      .catch(err => console.log(err));
  };
};

export const getCampgroundDetails = id => {
  return dispatch => {
    axios
      .get(`${API_URL}/campgrounds/${id}`)
      .then(res => {
        dispatch({
          type: GET_CAMPGROUND_DETAILS,
          payload: res
        });
      })
      .catch(err => console.log(err));
  };
};

export const actionUpdateCampground = id => {
  return dispatch => {
    axios
      .put(`${API_URL}/campgrounds/${id}`)
      .then(res => {
        console.log(res);
        dispatch({
          type: EDIT_CAMPGROUND,
          payload: res
        });
      })
      .catch(err => console.log(err));
  };
};


campgroundReducers.js

import {
  GET_ALL_CAMPGROUNDS,
  ADD_CAMPGROUND,
  GET_CAMPGROUND_DETAILS,
  EDIT_CAMPGROUND
} from '../actionTypes/types';

const initialState = {
  campgrounds: []
};

export default (state = initialState, action) => {
  switch (action.type) {
    case GET_ALL_CAMPGROUNDS:
      const { campgroundList } = action.payload.data;
      state.campgrounds = campgroundList;
      return {
        ...state
      };
    case ADD_CAMPGROUND:
      const { campground } = action.payload.data;
      return {
        ...state,
        campground
      };
    case GET_CAMPGROUND_DETAILS:
      const { singleCampground } = action.payload.data;
      return { ...state, singleCampground };
    case EDIT_CAMPGROUND:
      const { editedCampground } = action.payload.data;
      return { ...state, editedCampground };
    default:
      return state;
  }
};


如果我使用componentDidUpdate,它将导致无限循环。

componentDidUpdate(prevProps) {
  if (prevProps.campgrounds !== this.props.campgrounds) {
    this.props.getAllCampgrounds();
  }
}


我知道我在某个地方出错,但我不知道在哪里。

最佳答案

您必须修复componentDidUpdate方法来避免此无限循环。您正在尝试通过===方法比较对象,根据此示例,该方法将始终失败:



const a = {key: 1}
const b = {key: 1}
console.log(a === b); // false





您可以使用例如isEqual模块中的lodash方法来比较所需的对象,例如https://lodash.com/docs/4.17.15#isEqual



console.log(_.isEqual({key: 1}, {key: 1})) // true
console.log({key: 1} === {key: 1}) // false

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>





实际上,为什么要在这种情况下刷新数据?将对象成功添加到存储中后,这将刷新您的组件,因此您无需请求新数据。

10-08 16:24