Basically I want to show message to user after he successfully submitted form. Like Thanks. Product added. And after few seconds I want this message to disappear.
Currently my code is pretty straightforward and contains 3 action types for AJAX request (ADD_PRODUCT_REQUEST, ADD_PRODUCT_SUCCESS, ADD_PRODUCT_FAILURE).
My component containing form connected to redux via mapDispatchToProps:
import {addProduct} from '../actions/product';
const mapDispatchToProps = dispatch => ({
onSubmit(productName) {
dispatch(addProduct(productName))
}
});
class AddProduct extends React.Component {
addProduct() {
const {onSubmit} = this.props;
onSubmit(this.productNameInput.val);
}
render() {
return (
<form onSubmit={::this.addProduct}>...</form>
)
}
}
And my action creator is also pretty straightforward (pseudocode):
export const addProduct = (name) => dispatch => {
dispatch(addProductRequest())
fetch(...).then(addProductSuccess()).catch(e => addProductFailure(error))
}
How I can using this "standard" react-redux architecture know that AJAX request executed successfully on component side?
I have only 1 idea - add some value to state informing that product was added, like added:true, but I think it's bad idea.
You must return fetch result in actions, then you need to wrap up then and catch statements to catch result of action, something like this:
addProduct() {
this.setState({ error: {}, isLoading: true });
this.props.onSubmit(this.productNameInput.val)
.then(res => this.setState({ isLoading: false, success: { title: "Success!", msg: "Product added!" } }))
.catch(err => this.setState({ isLoading: false, error: err.response.data.error} }));
}
You can bind in future this example handling to your form validation or to your frontend notification system.
Related
I am just trying to delete an item on my page. When I delete the item I get this Unhandled Rejection (TypeError): state.recipes is undefined message pointing to my reducer. When I refresh my page, the object is gone and the error disappears. The question is what is causing this error prior to the item deleting?
This is what happens after I click delete button, when I refresh the page the object is gone.
case 'DELETING_RECIPE_START':
return {
...state.recipes,
loading: true
}
case 'DELETE_RECIPE_SUCCESS':
This line -----> const recipes = state.recipes.filter(recipe => recipe.id !== action.payload.recipeId)
return {
...state, recipes,
loading: false
}
I was told in this case is to check your delete action on the backend. When I plugged in byebug, It is showing me which object am trying to delete, so hopefully its nothing there I need to worry about.
def destroy
recipe = Recipe.find(params[:id])
unless recipe.nil?
recipe.destroy
render json: recipe
else
render json: { error: "Property not found" }, status: 404
end
end
I did modify my delete action to the thunk asynchronous conventions, and I hope it's structured correctly. I will note when I run debugger before the return(dispatch) this issue with my error seems to happen after the return(dispatch) line.
export const deleteRecipe = (recipeId) =>{
const BASE_URL = `http://localhost:3001`
const RECIPES_URL =`${BASE_URL}/recipes`
debugger
return (dispatch) => {
dispatch({ type: "DELETING_RECIPE_START" })
fetch(`${RECIPES_URL}/${recipeId}`,{method: 'DELETE'})
.then(response =>{return response.json()})
.then(recipeId => dispatch({ type: 'DELETE_RECIPE_SUCCESS', payload: recipeId }))
.catch((error) => console.log.error(error))
};
}
Last here is my Recipe component with the delete button and the event handler associated.
class Recipe extends Component {
handleOnClick(){
this.props.deleteRecipe(this.props.recipe.id);
}
render(){
return(
<div>
<h3>Name: {this.props.recipe.name}</h3>
<p>Category:{this.props.recipe.category_id}</p>
<p>Chef Name: {this.props.recipe.chef_name}</p>
<p>Origin: {this.props.recipe.origin}</p>
<p>Ingredients: {this.props.recipe.ingredients}</p>
<button onClick={()=>this.handleOnClick()}>Delete</button>
</div>
)
}
}
export default Recipe
What can I do to correct this?
For those interested in the solution. I credit my cohort lead for this. There was some restructuring involved.
When debugger is placed in my it’ll indicate that a key is not provided for recipes…well here is what it meant.
My DELETE_RECIPE_START case was like this at first
case 'DELETING_RECIPE_START':
return {
...state.recipes,
loading: true
}
It needed to look likes this
case 'DELETING_RECIPE_START':
return {
recipe:[...state.recipes],
loading: true
}
The recipe is the key while its current state is the value
The next part blew my mind…The delete action did not need a json response. You are only telling it to delete an id and that's it.
export const deleteRecipe = (recipeId) =>{
const BASE_URL = `http://localhost:3001`
const RECIPES_URL =`${BASE_URL}/recipes`
return (dispatch) => {
fetch(`${RECIPES_URL}/${recipeId}`, { method: 'DELETE' })
.then(() => {
return dispatch({ type: 'DELETE_RECIPE_SUCCESS', payload: recipeId })
});
};
}
I am really trying to get better at this but I enjoy the fact that I am learning.
I want to create a profile page in my React app. The user data is in the state but I want to load the data from the API as I load the page.
I've tried tyo fetch the data with this.props.getUser(this.props.auth._id) in the Constructor or in ComponentDidMount, but it did not load.
The data does comes in through componentWillReceiveProps, but it does not load on the first page load. Although, if I refresh the page, the data comes in.
This is part of my profile.js:
class Profile extends Component {
state = {
_id: '',
name: '',
email: '',
username: '',
errors: {}
};
componentDidMount() {
this.loadCurrentUser();
}
loadCurrentUser = () => {
this.setState(this.props.getUser(this.props.auth._id));
};
// https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607
UNSAFE_componentWillReceiveProps(nextProps, nextState) {
console.log("received")
const { name, email, username } = nextProps.auth;
this.setState({ name, email, username });
}
// ...
// Some other code
// ...
const mapStateToProps = (state) => ({
auth: state.auth
});
export default connect(mapStateToProps, { getUser, logOut })(Profile);
My question is: How to load the page with the data I get from the API presented in the form fields?
Thank you
EDITED
I have edited my componentDidMount to use promises, but I still can not get it right. Now, My store gets the states right, but my component still does not get updated.
componentDidMount() {
this.props.getUser(this.props.auth.user)
.then(user => this.setState({ name: user.name, email: user.email, username: user.username }))
}
If I add a simple console.log, I still can not get the return from my query (getUser). This console.log is undefined.
componentDidMount() {
this.props.getUser(this.props.auth.user)
.then(user => console.log(user));
}
This is my getUser (src/actions/userActions.js):
export const getUser = _id => async dispatch => {
const res = await axios.get(`${process.env.REACT_APP_USERS_API}/api/v1/users/${_id}`);
dispatch({
type: GET_USER,
payload: res.data.data
});
};
The getUser action does not have return value. Instead, it updates the user data inside the redux store. So you shouldn't reply on the return value and set state from it.
Instead, dispatch the getUser action on page load so that the user data is updated and always access the data from the store (through this.props.auth). If there is an updated version of the user data, React handles the page re-render automatically:
componentDidMount() {
this.props.getUser(this.props.auth.user);
}
If for some reason, you need the user data to be saved in state (for example, you have a form on page where user can update username/password), then use getDerivedStateFromProps method:
static getDerivedStateFromProps(props, state) {
// return an object to update the state.
return props.auth;
}
Basically I am creating a email form with react as view and express for the server. It is working fine.
Instead of redirecting on success I am just wanting to re render the form component on the contact page.
Is there anyway I can access the post request success value from within a react component? maybe calling a fetch method within the contact component that fetchs at the '/contact' end point? will this trigger the app.post() method within my express.js file??
this is my post request on button submit within react:
handleFormSubmit = (name, email, text) => {
axios.post('/contact', {
name: name,
email: email,
text: text
}).then(res => {
this.setState({sent: true})
console.log(this.state);
}).catch(err => {
console.log(err);
})
}
this is my express js post:
server.post('/contact', async (req, res) => {
try {
sendMail(req.body).then(info => console.log(info))
// I am getting a response here just fine
console.log(res)
} catch (error) {
console.log('express line 25: ', error);
}
});
I am using Next.js and nodemailer with GMail
Thanks
Ok sorry guys, problem has nothing to do with any code, its next js setup, i built it and ran and seems to be returning fine!
I don't see your code, but if we use fetch inside a React component, try using componentDidMount:
class Contact extends React.Component {
constructor(props) {
super(props);
this.state = {data: null, isLoading: true};
}
// from the server
componentDidMount() {
fetch('/contact').then( response => response.json()
).then( data => {
this.setState({data, isLoading: false}); // es6 syntax same as this.setState({data: data, isLoading: false})
}).catch( err => {
throw new Error(err);
});
}
render() {
const {data, isLoading} = this.state;
if(isLoading){return (<div>Loading...</div>)} //no data fetched in the initial render
return (
<div>
{data}
</div>
);
}
}
Hopefully that will help you.
I have a React, Redux app which should work as a CRUD application. And a part of a CRUD application is the ability to update stuff and that's what I currently have trouble with. The PUT request works (can see the changes in Robomongo), but my app crashes afterwards and the problem lies in my reducer; Unhandled Rejection (TypeError): Cannot read property 'item' of undefined (yeah, item is not the best naming, sorry).
I'd like to walk you through the process of the PUT request, because code > text after all.
I will start where my action is created, because I guess you can figure out I have a form as my legit starting point.
So, here's my action (sorry for the wall of code)
Action:
import axios from 'axios'
import settings from '../../../../settings'
import { merge } from 'lodash'
axios.defaults.baseURL = settings.hostname
export function updateSettings(id, updatedValues, controller, door) {
const custom_name = updatedValues.custom_name
const location = updatedValues.location
const open_duration = updatedValues.open_duration
return (dispatch, getState) => {
const state = getState()
const door = state.fetchDoors.doors.find(val => val._id === id.itemId)
const controller = state.fetchDoors.controllers.find(
controller => controller._id === door.controller
)
console.log('door', door) // Returns updated object
console.log('controller', controller) // Returns updated object
const doorPayload = {
...door,
custom_name,
location
}
const controllerPayload = {
...controller,
open_duration
}
axios
.put(`${settings.hostname}/locks/${id.itemId}`, doorPayload)
.then(res => {
dispatch({ type: 'DOOR_UPDATING' })
dispatch({
type: 'DOOR_UPDATED_SUCCESS',
doorPayload
})
})
axios
.put(
`${settings.hostname}/controllers/${door.controller}`,
controllerPayload
)
.then(res => {
dispatch({ type: 'CONTROLLER_UPDATING' })
dispatch({
type: 'CONTROLLER_UPDATING_SUCCESS',
controllerPayload
})
})
.catch(err => console.log(err))
}
}
And here's my reducer
Reducer:
const initialState = {
isLoading: false
}
export const settings = (state = initialState, action) => {
switch (action.type) {
case 'DOOR_UPDATING':
return { ...state, isLoading: true }
case 'DOOR_UPDATED_SUCCESS':
return { ...state, item: action.payload.item, isLoading: false } // Here's where the error occurs
case 'CONTROLLER_UPDATING':
return { ...state, isLoading: true }
case 'CONTROLLER_UPDATING_SUCCESS':
return { ...state, item: action.payload.item, isLoading: false }
default:
return state
}
}
So the error occur inside of my reducer (I've added a comment) and I really don't understand why, now when the PUT request changes the data inside of my database. I assume there's something silly I'm missing, but I can't fix it. All help is really appreciated and if more code/ info needed just let me know.
Thanks for reading.
Edit:
Here's how my door object looks like:
In your reducer you are expecting and action with the shape of:
{type: 'something', payload: 'something else'}
But when you dispatch the action you don't have a property of payload.
this is what you are dispatching:
{
...door, // this will spread all properties of door (which doesn't have a property with the name payload)
custom_name,
location
}
Then you are trying to access action.payload.item hence you get the error:
Cannot read property 'item' of undefined
payload is never defined in your action (by the way nor item was).
Hello people of Stack Overlflow! I have a problem that I can't solve and I need your help.
My Problem: After I've sent a put request to the server nothing changes. I'm console.logging this inside of my backend console.log(ctx.request.body) and it is the same data as before the put request. Although I'm getting Status Code:200 OK in the network tab.
So, I have a form that should be able to update the data from the API. And inside of my form I have an onSubmit handler:
onSubmit={e => {
e.preventDefault()
onSubmit(id, item)
}}
Here I am calling my function handleSubmit and passing id and item. Id is the id of the object and item is the object itself.
And here's the component that uses the onSubmit:
class DoorSettingsContainer extends Component {
render() {
return (
<div>
<DoorSettingsForm
onSubmit={this.props.updateSettings}
item={this.props.location.state.item}
id={this.props.location.state.item._id}
/>
</div>
)
}
}
function mapStateToProps(state) {
return {
item: state.settings.item
}
}
function mapDispatchToProps(dispatch) {
return {
updateSettings: (id, value) => dispatch(updateSettings(id, value))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(
DoorSettingsContainer
)
And here's my action that handles the put request:
export function updateSettings(id, item) {
return dispatch => {
dispatch({ type: 'SETTINGS_IS_LOADING' })
console.log(dispatch)
console.log('FÖRE', item)
axios
.put(`${settings.hostname}/locks/${id}`, item)
.then(() => {
console.log(item)
dispatch({
type: 'SETTINGS_UPDATED',
payload: {
item,
id
}
})
console.log('EFTER', item) // It's still the same
})
.catch(err => console.log(err))
}
}
And finally, my snippet from my backend:
r.put('/' + key + '/:id', async (ctx, next) => {
console.log(key) // Outputs locks
console.log(ctx.request.body) // Same as before
await ctx.db
.collection(key)
.replaceOne({ _id: objectId(ctx.params.id) }, ctx.request.body)
ctx.body = { _id: ctx.params.id }
})
So my problem is that my put request wont work to 100%. Is it might because I'm using Redux forms? Or is my backend not configured to 100%?
Thanks for all the help!