问题
I am trying to set profile state through redux. However for some reason my axios is being called twice
my database profile.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
preference: [
{
type: String
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model("profile", ProfileSchema);
myCreatePreferences class
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import checkboxes from "./checkboxes";
import Checkbox from "./Checkbox";
import axios from "axios";
import { Redirect } from "react-router";
import { withRouter } from "react-router-dom";
import Select from "react-select";
import { getCurrentProfile } from "../../actions/profileActions";
const options = [
{ value: "Guns", label: "Guns" },
{ value: "Gay Marriage", label: "Gay Marriage" },
{ value: "Abortion", label: "Abortion" },
{ value: "IT", label: "IT" }
];
class CreatePreferences extends Component {
constructor() {
super();
this.state = {
selectedOption: [],
fireRedirect: false
};
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
e.preventDefault();
let tempArray = [];
for (let i = 0; i < this.state.selectedOption.length; i++) {
tempArray[i] = this.state.selectedOption[i].value;
}
const preference = {
tempArray
};
//axios
// .post("/api/profile/", { tempArray: tempArray })
//.then(res => res.data)
// .catch(err => console.log(err));
this.props.getCurrentProfile(preference);
this.setState({ fireRedirect: true });
}
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
};
render() {
const { selectedOption } = this.state;
console.log(selectedOption.value);
const { fireRedirect } = this.state;
return (
<div>
<form onSubmit={this.onSubmit}>
<Select
value={selectedOption}
isMulti
onChange={this.handleChange}
options={options}
/>
<input
type="submit"
className="btn btn-info btn-block mt-4"
value="Save Preferences"
/>
{fireRedirect && <Redirect to={"/"} />}
</form>
</div>
);
}
}
CreatePreferences.propTypes = {
profile: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
profile: state.profile
});
export default connect(
mapStateToProps,
{ getCurrentProfile }
)(withRouter(CreatePreferences));
my profileActionsclass
import axios from "axios";
import {
GET_PROFILE,
PROFILE_LOADING,
GET_ERRORS,
CLEAR_CURRENT_PROFILE
} from "./types";
//Get current profile
export const getCurrentProfile = preference => dispatch => {
dispatch(setProfileLoading());
axios
.post("/api/profile", preference)
.then(res =>
dispatch({
type: GET_PROFILE,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_PROFILE,
payload: { err }
})
);
};
//Profile Loading
export const setProfileLoading = () => {
return {
type: PROFILE_LOADING
};
};
//Clear Profile
export const clearCurrentProfile = () => {
return {
type: CLEAR_CURRENT_PROFILE
};
};
profileReducer.js
import {
GET_PROFILE,
PROFILE_LOADING,
CLEAR_CURRENT_PROFILE
} from "../actions/types";
const initialState = {
profile: null,
profiles: null,
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case PROFILE_LOADING:
return {
...state,
loading: true
};
case GET_PROFILE:
return {
...state,
profile: action.payload,
loading: false
};
case CLEAR_CURRENT_PROFILE:
return {
...state,
profile: null
};
default:
return state;
}
}
The index.js class redux store.
import { combineReducers } from "redux";
import authReducer from "./authReducer";
import errorReducer from "./errorReducer";
import profileReducer from "./profileReducer";
import postReducer from "./postReducer";
export default combineReducers({
auth: authReducer,
errors: errorReducer,
profile: profileReducer,
post: postReducer
});
When I posting data from the createPreference class via the profileActions through axios I am getting two axios post request. It first fills the preference as expected, however it makes another call instantly and the preference is again set to null.The console.log(of the call)
preference: Array(2), _id: "5bbc73011f67820748fcd9ab", user: "5bb87db33cb39a844f0ea46a", date: "2018-10-09T09:21:05.968Z", __v: 0}
Dashboard.js:20 {preference: null, _id: "5bbc73011f67820748fcd9ab", user: "5bb87db33cb39a844f0ea46a", date: "2018-10-09T09:21:05.968Z", __v: 0}
Any suggestions on how to fix this?
回答1:
Since I don't have access to your all of your code (and can't debug it), here's a better approach to fetching data. I've structured it closely to what you have and if you follow the working example, you should be able to eliminate the problem.
What I did:
- Renamed
onSubmit={this.onSubmit}to a more standard declarativethis.handleSubmitmethod - Called
this.setState()in thehandleSubmitclass method to removeselectedOptionvalue, then in the setState callback, calledgetCurrentProfile(value, history)(substitute thevaluewith yourtempArray) - Changed your
<input type="submit" ... />to a<button type="submit" ... /> - Added a
returnfor theaxios.get(...)call (I've also included anasync/awaitversion ofgetCurrentProfilewhich might be easier to understand -- also substitute theaxios.getcall for youraxios.postcall) - Removed
Redirectand instead placed a redirect inside theactioncreator ashistory.push('/');(once request has been successfully sent, it'll redirect the user back to "/" -- if error, no redirect) - Always keep your redux state as a 1:1. In other words, if it's an array, then it stays an array (not
null), if it's a string, it stays a string (notnumber)...etc. When usingPropTypes,your app will throw errors if you do not keep this 1:1 pattern. For example, you're initially settingprofile: null, but then you set it asprofile: [ Object, Object, Object ... ]. Instead, it should initially be:profile: []. - When using
PropTypes, avoid ambiguous types such asobjectorarrayand instead describe how they are structured. - Due to the nature of redux and how you're setting your component up, you don't need to dispatch
setProfileLoading. You can just update your data and the connected React component will update to reflect the new change. Dispatching two redux actions separately within a short period of time will most likely lead to component flashing (think of it as callingthis.setState()twice within a second of each other -- it'll cause your component to flash).
Working example: https://codesandbox.io/s/ovjq7k7516
SelectOption.js
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";
import Select from "react-select";
import { clearCurrentProfile, getCurrentProfile } from "../actions";
const options = [
{ value: "todos?userId=1", label: "Todos" },
{ value: "comments?postId=1", label: "Comments" },
{ value: "users?id=1", label: "Users" },
{ value: "albums?userId=1", label: "Albums" }
];
class SelectOption extends Component {
state = {
selectedOption: []
};
handleSubmit = e => {
e.preventDefault();
const { getCurrentProfile, history } = this.props;
const { value } = this.state.selectedOption;
this.setState({ selectedOption: [] }, () =>
getCurrentProfile(value, history)
);
};
handleChange = selectedOption => this.setState({ selectedOption });
render = () => (
<div className="container">
<form onSubmit={this.handleSubmit}>
<Select
value={this.state.selectedOption}
onChange={this.handleChange}
options={options}
/>
<div className="save-button">
<button type="submit" className="uk-button uk-button-primary">
Save Preferences
</button>
</div>
<div className="clear-button">
<button
type="button"
onClick={this.props.clearCurrentProfile}
className="uk-button uk-button-danger"
>
Reset Preferences
</button>
</div>
</form>
</div>
);
}
export default connect(
state => ({ profile: state.profile }),
{ clearCurrentProfile, getCurrentProfile }
)(withRouter(SelectOption));
SelectOption.propTypes = {
clearCurrentProfile: PropTypes.func.isRequired,
getCurrentProfile: PropTypes.func.isRequired,
profile: PropTypes.shape({
profile: PropTypes.arrayOf(PropTypes.object),
profiles: PropTypes.arrayOf(PropTypes.object),
loading: PropTypes.bool
}).isRequired
};
actions/index.js
import axios from "axios";
import { GET_PROFILE, PROFILE_LOADING, CLEAR_CURRENT_PROFILE } from "../types";
//Get current profile
export const getCurrentProfile = (preference, history) => dispatch => {
// dispatch(setProfileLoading()); // not needed
return axios
.get(`https://jsonplaceholder.typicode.com/${preference}`)
.then(res => {
dispatch({
type: GET_PROFILE,
payload: res.data
});
// history.push("/") // <== once data has been saved, push back to "/"
})
.catch(err =>
dispatch({
type: GET_PROFILE,
payload: { err }
})
);
};
//Get current profile (async/await)
// export const getCurrentProfile = (preference, history) => async dispatch => {
// try {
// dispatch(setProfileLoading()); // not needed
// const res = await axios.get(
// `https://jsonplaceholder.typicode.com/${preference}`
// );
// dispatch({
// type: GET_PROFILE,
// payload: res.data
// });
// // history.push("/") // <== once data has been saved, push back to "/"
// } catch (e) {
// dispatch({
// type: GET_PROFILE,
// payload: { e }
// });
// }
// };
//Profile Loading
export const setProfileLoading = () => ({ type: PROFILE_LOADING });
//Clear Profile
export const clearCurrentProfile = () => ({ type: CLEAR_CURRENT_PROFILE });
reducers/index.js
import { combineReducers } from "redux";
import { CLEAR_CURRENT_PROFILE, GET_PROFILE, PROFILE_LOADING } from "../types";
const initialState = {
profile: [],
profiles: [],
loading: false
};
const profileReducer = (state = initialState, { type, payload }) => {
switch (type) {
case PROFILE_LOADING:
return {
...state,
loading: true
};
case GET_PROFILE:
return {
...state,
profile: payload,
loading: false
};
case CLEAR_CURRENT_PROFILE:
return {
...state,
profile: []
};
default:
return state;
}
};
export default combineReducers({
profile: profileReducer
});
来源:https://stackoverflow.com/questions/52729343/why-is-axios-being-called-twice-in-my-program