I've found a few other posts with similar errors and I've already ruled out:
The function is correctly imported in curly braces
The action is dispatched
The file path is correct
It must just be some silly mistake but Ive been looking at it for ages and cannot seem to find it, and help appreciated!
action:
export const deleteRow = (rowId) => (dispatch) => {
dispatch({ type: "LOADING_UI" });
axios
.delete(`/row/${rowId}`)
.then((res) => {
dispatch({
type: "DELETE_ROW",
payload: res.data,
});
dispatch(clearErrors());
})
.catch((err) => {
dispatch({
type: "SET_ERRORS",
payload: err.response.data,
});
});
};
component:
export class MyDelete extends Component {
state = {
open: false,
};
handleOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
handleDelete = () => {
this.props.deleteRow(this.props.rowId);
this.setState({ open: false });
};
render() {
return (
<Fragment>
<Button onClick={this.handleOpen}>
<HighlightOffIcon color="primary" />
</Button>
<Dialog
open={this.state.open}
onClose={this.handleClose}
fullWidth
maxWidth="sm"
>
<DialogTitle>Are you sure you want to delete this row ?</DialogTitle>
<DialogActions>
<Button onClick={this.handleClose} color="primary">
Cancel
</Button>
<Button onClick={this.handleDelete} color="secondary">
Delete
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
}
}
MyDelete.propTypes = {
deleteRow: PropTypes.func.isRequired,
rowId: PropTypes.string.isRequired,
};
export default connect(null, { deleteRow })(MyDelete);
You are using connect with object shorthand form, it expects each field to be an action creator, and binds each field to dispatch.
Your function deleteRow is not an action creator, so instead you should not use object shorthand form and directly use dispatch:
export class MyDelete extends Component {
//...
deleteRow = (rowId) => {
this.props.dispatch({ type: "LOADING_UI" });
axios.delete(...)
}
//...
}
export default connect()(MyDelete);
Try using the mapDispatchToProps function approach instead of object shorthand form like so :-
const mapDispatchToProps = (dispatch) =>{
return {
deleteRow:(rowId)=>dispatch(deleteRow(rowId));
}
}
export default connect(null, mapDispatchToProps)(MyDelete);
Related
I'm trying to learn the MERN stack, and I'm going through this tutorial on YouTube. I'm getting stuck in Ep. 7. The issue I think I'm having is my ADD_ITEM action is never triggered, and so the state is never updated, and I have to reload the page in order to see any items added. The DELETE_ITEM action works properly, so I suspect there may be an issue with the ADD_ITEM action being called from a form in a modal, but I'm unsure.
Picture of my Redux DevTools after refreshing the page, deleting 2 items, and trying to add 1:
itemReducer.js
import { GET_ITEMS, ADD_ITEM, DELETE_ITEM, ITEMS_LOADING } from '../actions/types';
const initialState = {
items: [],
loading: false
};
export default function(state = initialState, action) {
console.log(action.type);
switch(action.type) {
case GET_ITEMS:
return { ...state, items: action.payload, loading: false };
case ADD_ITEM:
return { ...state, items: [action.payload, ...state] };
case DELETE_ITEM:
return { ...state,
items: state.items.filter(item => item._id !== action.payload)
};
case ITEMS_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
ItemModal.js
import React, { Component } from 'react';
import { Button, Modal, ModalHeader, ModalBody, Form, FormGroup, Label, Input } from 'reactstrap';
import { connect } from 'react-redux';
import { addItem } from '../actions/itemActions';
class ItemModal extends Component {
state = {
modal: false,
name: ''
}
toggle = () => {
this.setState({
modal: !this.state.modal
});
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
onSubmit = (e) => {
e.preventDefault();
const newItem = {
name: this.state.name
}
this.props.addItem(newItem);
this.toggle();
}
render() {
return(
<div>
<Button
color="dark"
style={{marginBotton: '2rem'}}
onClick={this.toggle}
>Add Item</Button>
<Modal
isOpen={this.state.modal}
toggle={this.toggle}
>
<ModalHeader
toggle={this.toggle}
>
Add To Shopping List
</ModalHeader>
<ModalBody>
<Form onSubmit={this.onSubmit}>
<FormGroup>
<Label for="item">Item</Label>
<Input
type="text"
name="name"
id="item"
placeholder="Add shopping item"
onChange={this.onChange}
/>
</FormGroup>
<Button
color="dark"
style={{marginTop: '2rem'}}
block
>Submit</Button>
</Form>
</ModalBody>
</Modal>
</div>
)
}
}
const mapStateToProps = state => ({
item: state.item
});
export default connect(mapStateToProps, { addItem })(ItemModal);
itemActions.js
import axios from 'axios';
import { GET_ITEMS, ADD_ITEM, DELETE_ITEM, ITEMS_LOADING } from './types';
export const getItems = () => dispatch => {
dispatch(setItemsLoading());
axios.get('/api/items').then(res => dispatch({
type: GET_ITEMS,
payload: res.data
}))
};
export const deleteItem = (id) => dispatch => {
axios.delete(`/api/items/${id}`).then(res => dispatch({
type: DELETE_ITEM,
payload: id
}))
};
export const addItem = (item) => dispatch => {
axios.post('/api/items', item).then(res => dispatch({
type: ADD_ITEM,
payload: res.data
}))
};
export const setItemsLoading = () => {
return {
type: ITEMS_LOADING
};
};
I am using redux to get the async data and response. In the below component when i post recipe and from server i get response through redux the success modal popup twice. reducer is running only once i have checked eveything, only component has problem. the problem could be with the lifecycle method.
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import {withRouter} from 'react-router-dom';
import {connect} from 'react-redux';
import * as actionCreators from '../../actions/recipe-action/index';
import { Modal, Button } from "antd";
import Spinner from '../../UI/spinner';
class PostRecipe extends Component {
state = {
url: '',
visible: false,
}
showModal = () => {
this.setState({ visible: true });
};
onChangeHandler = (e) => {
this.setState({[e.target.name]: e.target.value});
}
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: ""});
this.setState({ visible: false });
};
handleCancel = e => {
this.setState({ visible: false });
};
render() {
const { postRecipes } = this.props;
if(postRecipes.loading) {
return <Spinner />;
}else if(postRecipes.success.ok) {
// this success model popup twice after uploading the recipe
Modal.success({
content: "Recipe Uploaded"
});
}else if(postRecipes.failure.error) {
Modal.error({
title: "Error while uploading recipe",
});
}
return (
<div>
<div>
<Button type="primary" onClick={this.showModal}>
Add Recipe
</Button>
<Modal
title="Add Recipe"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<input
style={{ width: "100%", padding: "5px", fontSize: "15px" }}
type="text"
placeholder="enter the url"
name="url"
value={this.state.url}
onChange={this.onChangeHandler}
/>
</Modal>
</div>
</div>
);
}
}
const mapStateToProps = ({ postRecipeReducers }) => {
const { recipe: { post: postRecipes } } = postRecipeReducers;
return {
postRecipes
}
};
const mapStateToDispatch = dispatch => {
return {
recipe: (url) => dispatch(actionCreators.postRecipes(url))
}
}
export default withRouter(connect(mapStateToProps, mapStateToDispatch)(PostRecipe));
// my action creators
import {POST_RECIPE_LOADING, POST_RECIPE_SUCCESS, POST_RECIPE_FAILURE, POST_RECIPE_RESET} from '../types';
import {GET_RECIPE_LOADING, GET_RECIPE_SUCCESS, GET_RECIPE_FAILURE, GET_RECIPE_RESET} from '../types';
import Parse from 'parse';
export const postRecipes = (url) => async(dispatch) => {
try {
dispatch({type: POST_RECIPE_LOADING, payload: null});
const {data} = await Parse.Cloud.run('post_recipe', {url: url});
dispatch({type: POST_RECIPE_SUCCESS, payload: data});
} catch(e) {
dispatch({type: POST_RECIPE_FAILURE, payload: {message: e.message}})
}
}
export const getRecipes = () => async (dispatch) => {
try {
dispatch({type: GET_RECIPE_LOADING, payload: null});
const {data} = await Parse.Cloud.run('get_recipe');
dispatch({type: GET_RECIPE_SUCCESS, payload: data});
} catch(e) {
dispatch({type: GET_RECIPE_FAILURE, payload: {message: e.message}})
}
};
Try this:
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: "", visible: false});
};
state variable of class is a object with two keys: url and visible. You have to set both at once.
I would try implementing a constructor function to make sure that you have this bound to your local state.
In this code block,
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: ""});
this.setState({ visible: false });
};
you could set the whole state in one line like this,
handleOk = e => {
this.props.recipe(this.state.url);
this.setState({url: "", visible: false});
}
I don't know that this will fix your problem. Just a bit of house keeping.
I'm updating a project from a MERN stack course I completed earlier. It has a 'Like' and 'Dislike' button, which update in MongoDB, and then the updated number of likes gets rendered to the screen. Right now, it's updating the database, but not re-rendering the updated information on the screen.
For example, I'll hit the like button on a post (should take it from 2 likes to 3). I can see the updated information in MongoDB (3), but it never adds the like on the screen (2). Additionally, Redux DevTools tells me it didn't update the state automatically (still 2). I then hit the 'refresh' button on my browser, and the updated number of likes is there (3).
I used this component and these actions/reducers in another component, and they work perfectly. For some reason, it's behaving differently here. Maybe something to do with the props I'm passing in? UseEffect issue?
I tried console.logging the likes property (post.likes) before and after the PostLikeAndDislike component, and it's the same number of likes both times (that is, if it's 3 before I click, it's 3 after I click).
PostLikeAndDislike component:
const PostLikeAndDislike = ({
addLike,
removeLike,
deletePost,
auth,
post: { _id, text, name, avatar, user, likes, comments, date },
showActions
}) => (
<div className="post bg-white p-1 my-1">
<div>
{showActions && (
<Fragment>
<button
onClick={e => addLike(_id)}
type="button"
className="btn btn-light"
>
<i className="fas fa-thumbs-up" />{' '}
{likes.length > 0 && <span>{likes.length}</span>}
</button>
<button
onClick={e => removeLike(_id)}
type="button"
className="btn btn-light"
>
<i className="fas fa-thumbs-down" />
</button>
</Fragment>
)}
</div>
</div>
)
PostLikeAndDislike.defaultProps = {
showActions: true
}
const mapStateToProps = state => ({
auth: state.auth
})
export default connect(
mapStateToProps,
{ addLike, removeLike, deletePost }
)(PostLikeAndDislike)
The component PostLikeAndDislike is being imported into:
const Post = ({ getPost, post: { post, loading }, match }) => {
useEffect(
() => {
getPost(match.params.id)
},
[getPost]
)
return loading || post === null ? (
<Spinner />
) : (
<Fragment>
{console.log('Before', post.likes)}
<PostLikeAndDislike post={post} showActions={true} />
<div className="comments">
{post.comments.map(comment => (
<CommentItem key={comment._id} comment={comment} postId={post._id} />
))}
</div>
{console.log('After', post.likes)}
<CommentForm postId={post._id} />
</Fragment>
)
}
const mapStateToProps = state => ({
post: state.post
})
export default connect(
mapStateToProps,
{ getPost, addLike, removeLike }
)(Post)
Actions:
// Get post
export const getPost = id => async dispatch => {
try {
const res = await axios.get(`/api/posts/${id}`)
dispatch({
type: GET_POST,
payload: res.data
})
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { msg: err.response.statusText, status: err.response.status }
})
}
}
// Add like
export const addLike = id => async dispatch => {
try {
const res = await axios.put(`/api/posts/like/${id}`)
dispatch({
type: UPDATE_LIKES,
payload: { id, likes: res.data }
})
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { msg: err.response.statusText, status: err.response.status }
})
}
}
// Remove like
export const removeLike = id => async dispatch => {
try {
const res = await axios.put(`/api/posts/unlike/${id}`)
dispatch({
type: UPDATE_LIKES,
payload: { id, likes: res.data }
})
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
loading: false
})
}
}
Reducer:
const initialState = {
posts: [],
post: null,
loading: true,
error: {}
}
case GET_POST:
return {
...state,
post: payload,
loading: false
}
case UPDATE_LIKES:
return {
...state,
posts: state.posts.map(
post =>
post._id === payload.id ? { ...post, likes: payload.likes } : post
)
}
Like I said above, I'm expecting the 'like' button and 'dislike' button to change the number of likes and re-render it to the screen immediately.
Thanks in advance!
For learning purpose I made this web app where I'm trying to implement crud operations. All works properly except UPDATE, where MongoDB record is updated but changes on the screen are not reflected till the refresh.
I'm still learning therefore not everything is crystal clear, I'm suspecting a problem in a REDUCER... or in the component mapStateToProp object...
What am I doing wrong here?
routes/api
Item.findByIdAndUpdate for sure update's db correctly, but should it also return anything so the reducer/action could react to it?
const express = require("express");
const router = express.Router();
const auth = require("../../middleware/auth");
// Item Model
const Item = require("../../models/stories");
// #route GET api/items
// #desc Get All Items
// #access Public
router.get("/", (req, res) => {
Item.find()
.sort({ date: -1 })
.then(items => res.json(items));
});
// #route PUT api/items
// #desc Update An Item
// #access Private
router.put("/:_id", auth, (req, res) => {
Item.findByIdAndUpdate(
req.params._id,
req.body,
{ new: false, useFindAndModify: false },
() => {}
);
});
module.exports = router;
reducers
import {
GET_STORIES,
ADD_STORY,
DELETE_STORY,
STORIES_LOADING,
UPDATE_STORY
} from "../actions/types";
const initialState = {
stories: [],
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_STORIES:
return {
...state,
stories: action.payload,
loading: false
};
case DELETE_STORY:
return {
...state,
stories: state.stories.filter(story => story._id !== action.payload)
};
case ADD_STORY:
return {
...state,
stories: [action.payload, ...state.stories]
};
case UPDATE_STORY:
return {
...state,
stories: action.payload
};
case STORIES_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
actions
import axios from "axios";
import {
GET_STORIES,
ADD_STORY,
DELETE_STORY,
UPDATE_STORY,
STORIES_LOADING
} from "./types";
import { tokenConfig } from "./authActions";
import { returnErrors } from "./errorActions";
export const getStories = () => dispatch => {
dispatch(setStoriesLoading());
axios
.get("/api/stories")
.then(res =>
dispatch({
type: GET_STORIES,
payload: res.data
})
)
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const addStory = story => (dispatch, getState) => {
axios
.post("/api/stories", story, tokenConfig(getState))
.then(res => {
dispatch({
type: ADD_STORY,
payload: res.data
});
})
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const updateStory = story => (dispatch, getState) => {
axios
.put(`/api/stories/${story.id}`, story, tokenConfig(getState))
.then(res => {
dispatch({
type: UPDATE_STORY,
payload: story
});
})
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const deleteStory = id => (dispatch, getState) => {
axios
.delete(`/api/stories/${id}`, tokenConfig(getState))
.then(res => {
dispatch({
type: DELETE_STORY,
payload: id
});
})
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const setStoriesLoading = () => {
return {
type: STORIES_LOADING
};
};
component
import React, { Component } from "react";
import {
Modal,
ModalHeader,
ModalBody,
Form,
FormGroup,
Label,
Input
} from "reactstrap";
import { connect } from "react-redux";
import { updateStory } from "../../actions/storyActions";
import PropTypes from "prop-types";
class UpdateStoryModal extends Component {
constructor(props) {
super(props);
}
state = {
id: this.props.idVal,
modal: false,
title: this.props.titleVal,
body: this.props.bodyVal
};
static propTypes = {
isAuthenticated: PropTypes.bool
};
toggle = () => {
this.setState({
modal: !this.state.modal
});
};
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
onSubmit = e => {
e.preventDefault();
const obj = {
id: this.props.idVal,
title: this.state.title,
body: this.state.body
};
this.props.updateStory(obj);
this.toggle();
};
render() {
return (
<div>
{this.props.isAuthenticated ? (
<button
type="button"
className="btn btn-primary"
size="sm"
onClick={this.toggle}
>
Edit Story
</button>
) : (
<h4 className="mb-3 ml-4">Please log in to manage stories</h4>
)}
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Edit story</ModalHeader>
<ModalBody>
<Form>
<FormGroup>
<Label for="story">Title</Label>
<Input
type="text"
name="title"
id="story"
onChange={this.onChange}
value={this.state.title}
/>
<Label for="story">Story</Label>
<Input
type="textarea"
name="body"
rows="20"
value={this.state.body}
onChange={this.onChange}
/>
<button
type="button"
className="btn btn-dark"
style={{ marginTop: "2rem" }}
onClick={this.onSubmit}
>
Edit story
</button>
</FormGroup>
</Form>
</ModalBody>
</Modal>
</div>
);
}
}
const mapStateToProps = state => ({
story: state.story,
isAuthenticated: state.auth.isAuthenticated
});
export default connect(
mapStateToProps,
{ updateStory }
)(UpdateStoryModal);
Yes, you want to return the updated item from your MongoDB database so that you have something to work with in your reducer. It looks like you've setup your action-creator to be prepared for that type of logic. So we just need to make a couple updates:
In your express route you would want something like:
router.put("/:_id", auth, (req, res) => {
//this returns a promise
Item.findByIdAndUpdate(
req.params._id,
req.body,
{ new: false, useFindAndModify: false },
() => {}
)
.then((updatedItem) => {
res.json(updatedItem) //we capture this via our promise-handler on the action
})
.catch((error) => {
return res.status(400).json({ couldnotupdate: "could not update item"})
})
});
Then we can tap into that updated item using res.data in your action-creator promise-handler
export const updateStory = story => (dispatch, getState) => {
axios
.put(`/api/stories/${story.id}`, story, tokenConfig(getState))
.then(res => {
dispatch({
type: UPDATE_STORY,
payload: res.data
});
})
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
Now that you have the updated item as an action-payload, we need to update your reducer:
case UPDATE_STORY:
return {
...state,
stories: state.stories.map((story) => {
if(story._id == action.payload._id){
return{
...story,
...action.payload
} else {
return story
}
}
})
};
With that you should be able to take the updated story from your back-end and have it reflected to the front.
REACT Redux PUT request gives me following error:
"Proxy error: Could not proxy request /api/stories/5ccf12b5f6b087c2a3fcc21b from localhost:3000 to http://localhost:5002.
[1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET)."
I made sample React Redux CRUD app based on #Brad Traversy tutorial and GET, POST, DELETE requests works fine.
API with mongoose:
const express = require("express");
const router = express.Router();
const auth = require("../../middleware/auth");
// Item Model
const Item = require("../../models/stories");
// #route GET api/items
// #desc Get All Items
// #access Public
router.get("/", (req, res) => {
Item.find()
.sort({ date: -1 })
.then(items => res.json(items));
});
// #route POST api/items
// #desc Create An Item
// #access Private
router.post("/", auth, (req, res) => {
const newItem = new Item({
title: req.body.title,
body: req.body.body,
author: "Les Frazier"
});
newItem.save().then(item => res.json(item));
});
router.put("/:_id", auth, (req, res) => {
var query = { _id: req.params._id };
var update = {
_id: req.params._id,
title: req.params.title,
body: req.params.body
};
var options = { new: true, useFindAndModify: false };
Item.findByIdAndUpdate(req.params._id, { $set: update }, options);
});
// #route DELETE api/items/:id
// #desc Delete A Item
// #access Private
router.delete("/:id", auth, (req, res) => {
Item.findById(req.params.id)
.then(item => item.remove().then(() => res.json({ success: true })))
.catch(err => res.status(404).json({ success: false }));
});
module.exports = router;
Child component for updating(PUT) data that is failing
import React, { Component } from "react";
import {
Button,
Modal,
ModalHeader,
ModalBody,
Form,
FormGroup,
Label,
Input
} from "reactstrap";
import { connect } from "react-redux";
import { updateStory, deleteStory } from "../../actions/storyActions";
import PropTypes from "prop-types";
class UpdateStoryModal extends Component {
state = {
modal: false,
title: this.props.story.stories.find(
story => story._id === this.props.value
).title,
body: this.props.story.stories.find(story => story._id === this.props.value)
.body
};
static propTypes = {
isAuthenticated: PropTypes.bool
};
toggle = () => {
this.setState({
modal: !this.state.modal
});
};
onChange = e => {
e.target.name === "title"
? this.setState({ title: e.target.value })
: this.setState({ body: e.target.value });
};
//PUT
onSubmit = e => {
e.preventDefault();
const updateStory = {
_id: this.props.value,
title: this.state.title,
body: this.state.body
};
this.props.updateStory(updateStory);
// Close modal
this.toggle();
};
render() {
return (
<div>
{this.props.isAuthenticated ? (
<Button color="primary" size="sm" onClick={this.toggle}>
Edit Story
</Button>
) : (
<h4 className="mb-3 ml-4">Please log in to manage stories</h4>
)}
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Edit story</ModalHeader>
<ModalBody>
<Form onSubmit={this.onSubmit}>
<FormGroup>
<Label for="story">Title</Label>
<Input
type="text"
name="title"
id="story"
onChange={this.onChange}
value={this.state.title}
/>
<Label for="story">Story</Label>
<Input
type="textarea"
name="body"
rows="20"
value={this.state.body}
onChange={this.onChange}
/>
<Button color="dark" style={{ marginTop: "2rem" }} block>
Edit story
</Button>
</FormGroup>
</Form>
</ModalBody>
</Modal>
</div>
);
}
}
const mapStateToProps = state => ({
story: state.story,
title: state.title,
body: state.body,
isAuthenticated: state.auth.isAuthenticated
});
export default connect(
mapStateToProps,
{ updateStory, deleteStory }
)(UpdateStoryModal);
Child component that is adding(POST) data and works fine
import React, { Component } from "react";
import {
Button,
Modal,
ModalHeader,
ModalBody,
Form,
FormGroup,
Label,
Input
} from "reactstrap";
import { connect } from "react-redux";
import { addStory } from "../../actions/storyActions";
import PropTypes from "prop-types";
class AddStoryModal extends Component {
state = {
modal: false,
title: "",
body: ""
};
static propTypes = {
isAuthenticated: PropTypes.bool
};
toggle = () => {
this.setState({
modal: !this.state.modal
});
};
onChange = e => {
e.target.name === "title"
? this.setState({ title: e.target.value })
: this.setState({ body: e.target.value });
};
onSubmit = e => {
e.preventDefault();
const newStory = {
title: this.state.title,
body: this.state.body
};
this.props.addStory(newStory);
// Close modal
this.toggle();
};
render() {
return (
<div>
{this.props.isAuthenticated ? (
<Button
color="dark"
style={{ marginBottom: "2rem" }}
onClick={this.toggle}
>
Add Story
</Button>
) : (
<h4 className="mb-3 ml-4">Please log in to manage stories</h4>
)}
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Add new story</ModalHeader>
<ModalBody>
<Form onSubmit={this.onSubmit}>
<FormGroup>
<Label for="story">Title</Label>
<Input
type="text"
name="title"
id="story"
onChange={this.onChange}
/>
<Label for="story">Story</Label>
<Input
type="textarea"
name="body"
rows="20"
onChange={this.onChange}
/>
<Button color="dark" style={{ marginTop: "2rem" }} block>
Add Story
</Button>
</FormGroup>
</Form>
</ModalBody>
</Modal>
</div>
);
}
}
const mapStateToProps = state => ({
title: state.title,
body: state.body,
isAuthenticated: state.auth.isAuthenticated
});
export default connect(
mapStateToProps,
{ addStory }
)(AddStoryModal);
Reducer
import {
GET_STORIES,
ADD_STORY,
DELETE_STORY,
STORIES_LOADING,
UPDATE_STORY
} from "../actions/types";
const initialState = {
stories: [],
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_STORIES:
return {
...state,
stories: action.payload,
loading: false
};
case DELETE_STORY:
return {
...state,
stories: state.stories.filter(story => story._id !== action.payload)
};
case ADD_STORY:
return {
...state,
stories: [action.payload, ...state.stories]
};
case UPDATE_STORY:
return {
...state,
stories: state.stories.map(story =>
story._id === action.payload._id ? (story = action.payload) : story
)
};
case STORIES_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
Action
import axios from "axios";
import {
GET_STORIES,
ADD_STORY,
DELETE_STORY,
UPDATE_STORY,
STORIES_LOADING
} from "./types";
import { tokenConfig } from "./authActions";
import { returnErrors } from "./errorActions";
export const getStories = () => dispatch => {
dispatch(setStoriesLoading());
axios
.get("/api/stories")
.then(res =>
dispatch({
type: GET_STORIES,
payload: res.data
})
)
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const addStory = story => (dispatch, getState) => {
axios
.post("/api/stories", story, tokenConfig(getState))
.then(res =>
dispatch({
type: ADD_STORY,
payload: res.data
})
)
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const updateStory = story => (dispatch, getState) => {
axios
.put(`/api/stories/${story._id}`, story, tokenConfig(getState))
.then(res =>
dispatch({
type: UPDATE_STORY,
payload: res.data
})
)
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const deleteStory = id => (dispatch, getState) => {
axios
.delete(`/api/stories/${id}`, tokenConfig(getState))
.then(res =>
dispatch({
type: DELETE_STORY,
payload: id
})
)
.catch(err =>
dispatch(returnErrors(err.response.data, err.response.status))
);
};
export const setStoriesLoading = () => {
return {
type: STORIES_LOADING
};
};
Ok so I found main problem...
I have replaced this code"
Item.findByIdAndUpdate(req.params._id, { $set: update }, options);
with this:
Item.findByIdAndUpdate(
req.params._id,
req.body,
{ new: false, useFindAndModify: false },
() => {
console.log("done");
}
);
And the record is being updated now in db. So the main problem is gone.
But whenever I refresh a page I'm still getting this:
Proxy error: Could not proxy request /api/stories/5ccf398fe278beca5efa3d23 from localhost:3000 to http://localhost:5002.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).