我正在构建一个简单的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#isEqualconsole.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>
实际上,为什么要在这种情况下刷新数据?将对象成功添加到存储中后,这将刷新您的组件,因此您无需请求新数据。