.then promise not working within axios delete request in react application - javascript

I am trying to call a function to fetch data from the database upon deleting a note. This is so that the array of notes can be updated to reflect the deleted note. The function where the error occurs is called deleteNote and the function I am trying to call within the .then promise is getNotes.
Below is the code in my App.js file. If someone could help me solve this I'd greatly appreciate it.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
// import HighlightOffIcon from '#material-ui/icons/HighlightOff';
import './App.css';
const App = () => {
const [note, setNote] = useState('');
const [notesList, setNotesList] = useState([]);
const getNotes = () => {
axios.get('http://localhost:8080/api')
.then((res) => setNotesList(res.data))
.catch(() => alert('Error recieving data.'));
}
useEffect(() => {
getNotes();
}, [])
const handleChange = (event) => {
const content = event.target.value;
setNote(content);
}
const handleSubmission = (event) => {
event.preventDefault();
axios({
url: 'http://localhost:8080/api/save',
method: 'POST',
data: {
content: note
}
})
.then((res) => {
console.log('Created Note');
setNote('');
getNotes();
})
.catch(() => {
console.log('Internal server error');
})
}
const deleteNote = (event) => {
const value = event.target.value;
axios({
method: 'DELETE',
url: 'http://localhost:8080/api/delete',
data: {
_id: value
}
})
.then(() => {
console.log('Note Deleted');
getNotes(); //Where the notes should be fetched upon successful deletion.
})
.catch(() => {
alert('Error deleting note.');
});
}
return (
<div className="app">
<h1>React Notes App</h1>
<form onSubmit={handleSubmission}>
<input
type="text"
placeholder="Enter note"
value={note}
onChange={handleChange}
/>
<button className="submit-button">Submit</button>
</form>
<div className="notes-list">
{notesList.map((note, index) => {
return (
<div className="note" key={index}>
<p>{note.content}</p>
<button value={note._id} className="delete-button" onClick={deleteNote}><i className="fas fa-trash-alt"></i></button>
</div>
);
})}
</div>
</div>
);
}
export default App;

I figured out the issue. When sending a request with axios, you must have a response sent back from the server in order to execute any code you may have in the promise.
example server code:
app.delete('/delete', (req, res) => {
BlogPost.delete({_id: req.body.id}, (err) => {
if (err) {
console.log(err);
} else {
console.log('Successfully deleted blog post.')
res.json({ //Must include a response to execute code within the axios promise.
msg: 'Delete request was recieved.'
});
}
});
});

Related

How to have conditional defaultValues from useEffect in react-select within react-hook-form?

I'm working on a form using react-hook-form that contains a react-select CreatableSelect multiselect input. The multiselect is used for tags of a given post and it is conditional based on if the user selects to update the tags of an existing post.
My issue is that the defaultValue for the multiselect is not working when a user selects an existing post that contains tags.
The overall flow is: User selects existing post (in PublicShareNetworkSelect in my example) > onChange function changes the post ID stored in hook (selectedNetwork in my example) > change in selectedNetwork fires getNetworkData function that sets the tags variable (networkTags) used as the multiselect defaultValue
Also the getTags() function is used to populate the options in the multiselect.
I believe that the issue as something to do with getting the data from the APIs because I tried to create a minimum reproducible example, but it works exactly how I want it to without the axios calls. However, when I console.log the allTags and networkTags in my full example, there are matching objects in the arrays (the matches should be the defaultValue).
Code example: Main/Parent form component
import React, { useState, useEffect } from "react";
import axios from "axios";
import Form from "react-bootstrap/Form";
import { useForm, Controller } from "react-hook-form";
import CreatableSelect from "react-select/creatable";
import Button from "react-bootstrap/Button";
import PublicShareNetworkSelect from "./publicShareNetworkSelect";
function PublicShareForm(props) {
const {
register,
handleSubmit,
reset,
control,
errors,
watch,
onChange,
} = useForm();
const [loading, setLoading] = useState(false);
const [selectedNetwork, setSelectedNetwork] = useState([]);
const [allTags, setAllTags] = useState();
const [networkTags, setNetworkTags] = useState([]);
//Create axios instance
const axiosSharedNetwork = axios.create();
async function getTags() {
const getAllTagsApi = {
url: "/public-share/get-all-tags",
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
method: "GET",
};
await axiosSharedNetwork(getAllTagsApi)
.then((response) => {
const resData = response.data;
const tags = resData.map((tag, index) => ({
key: index,
value: tag.tag_id,
label: tag.name,
}));
setAllTags(tags);
setLoading(false);
})
.catch((error) => {
console.log(error.response);
});
}
async function getNetworkData(networkId) {
const getNetworkDataApi = {
url: "/public-share/get-network/" + networkId,
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
method: "GET",
};
const getNetworkTagsApi = {
url: "/public-share/get-network-tags/" + networkId,
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
method: "GET",
};
await axiosSharedNetwork(getNetworkDataApi)
.then(async (response) => {
const resData = response.data;
//Set some variables (i.e. title, description)
await axiosSharedNetwork(getNetworkTagsApi)
.then(async (response) => {
const tagResData = response.data;
const tags = tagResData.map((tag, index) => ({
key: index,
value: tag.tag_id,
label: tag.name,
}));
setNetworkTags(tags);
setLoading(false);
})
.catch((error) => {
console.log(error.response);
});
})
.catch((error) => {
console.log(error.response);
});
}
useEffect(() => {
getTags();
getNetworkData(selectedNetwork);
reset({ tags: selectedNetwork });
}, [reset]);
async function onSubmit(data) {
//Handle submit stuff
}
console.log(allTags);
console.log(networkTags);
return (
<Form id="public-share-form" onSubmit={handleSubmit(onSubmit)}>
<Form.Group>
<Form.Label>Create New Version of Existing Shared Network?</Form.Label>
<PublicShareNetworkSelect
control={control}
onChange={onChange}
setSelectedNetwork={setSelectedNetwork}
/>
<Form.Label>Tags</Form.Label>
<Controller
name="tags"
defaultValue={networkTags}
control={control}
render={({ onChange }) => (
<CreatableSelect
isMulti
placeholder={"Select existing or create new..."}
onChange={(e) => onChange(e)}
options={allTags}
defaultValue={networkTags}
classNamePrefix="select"
/>
)}
/>
</Form.Group>
<Button variant="secondary" onClick={props.handleClose}>
Cancel
</Button>
<Button variant="primary" type="submit">
Share
</Button>
</Form>
);
}
export default PublicShareForm;
PublicShareNetworkSelect - the select component that triggers the function to set the existing post id (selectedNetwork):
import React, { useState, useEffect } from "react";
import axios from "axios";
import { Controller } from "react-hook-form";
import Select from "react-select";
function PublicShareNetworkSelect(props) {
const [loading, setLoading] = useState(false);
const [networks, setNetworks] = useState([]);
//Create axios instance
const axiosNetworks = axios.create();
// Add a request interceptor
axiosNetworks.interceptors.request.use(
function (config) {
// Do something before request is sent
setLoading(true);
return config;
},
function (error) {
// Do something with request error
setLoading(false);
return Promise.reject(error);
}
);
// Add a response interceptor
axiosNetworks.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
setLoading(true);
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
setLoading(false);
return Promise.reject(error);
}
);
async function getNetworks() {
const getNetworksApi = {
url: "public-share/get-user-networks/" + props.username,
method: "GET",
};
await axiosNetworks(getNetworksApi)
.then(async (response) => {
setNetworks(
response.data.map((network, index) => ({
key: index,
value: network.network_id,
label: network.title,
}))
);
setLoading(false);
})
.catch((error) => {
console.log(error.response);
});
}
useEffect(() => {
getNetworks();
}, []);
function handleChange(data) {
console.log(data);
if (data) {
props.setSelectedNetwork(data.value);
props.getNetworkData(data.value);
} else {
props.setNetworkTitle("");
props.setNetworkDesc("");
}
}
if (!loading) {
if (networks.length === 0) {
return (
<React.Fragment>
<br />
<p className="font-italic text-muted">
You haven't created any public networks yet.
</p>
</React.Fragment>
);
} else {
return (
<Controller
name="tags"
defaultValue={[]}
control={control}
render={(props) => (
<CreatableSelect
isMulti
placeholder={"Select existing or create new..."}
onChange={(e) => onChange(e)}
// defaultValue={networkTags}
options={allTags}
classNamePrefix="select"
{...props}
/>
)}
/>
);
}
} else {
return <React.Fragment>Loading...</React.Fragment>;
}
}
export default PublicShareNetworkSelect;
Edit 1: console.log output for allTags (options) and networkTags (defaultValue)
The problem is, defaultValue is cached at the first render. The same applies to defaultValues property passed to useForm.
Important: defaultValues is cached at the first render within the custom hook. If you want to reset the defaultValues, you should use the reset api.
As quote from the docs suggests - you have to use reset. I've modified your example accordingly. Take a look here. As you can see I'm asynchronously resetting the form and it works.
Also, pay attention to render prop of the Controller - I'm passing down all props given, not only onChange. It's so because there are other important thingies in here (like value). By wrapping your component in Controller you have to provide onChange and value pair at least.
If you want to read more about reset take a look here.

How can I POST data using API from REACTJS?

This is my react code here I want to POST Data using postPoll API and update polls state but I am not understand how can do that.
please help..! please help..!please help..!please help..!please help..!please help..!please help..! at line number 33, 34 ( handalchange )
import React, { useState, useEffect } from "react";
import Poll from "react-polls";
import "../../styles.css";
import { isAutheticated } from "../../auth/helper/index";
import { getPolls, postPoll } from "../helper/coreapicalls";
import axios from "axios";
import { API } from "../../backend";
const MainPoll = () => {
const userId = isAutheticated() && isAutheticated().user._id;
const [polls, setPoll] = useState([]);
const [error, seterror] = useState(false);
useEffect(() => {
loadPoll();
}, []);
const loadPoll = () => {
getPolls().then((data) => {
if (data.error) {
seterror(data.error);
} else {
setPoll(data);
console.log(data);
}
});
};
// Handling user vote
// Increments the votes count of answer when the user votes
const handalchange = async (pollId, userId, answer) => {
console.log(pollId); // getting
console.log(userId); // getting
console.log(answer); // getting
await axios.post(`${API}/vote/${pollId}`, userId, answer);
// postPoll(pollId, { userId, vote }).then(() => {
// loadPoll();
// });
};
return (
<div className="">
<div className="container my-5">
<h1 className="blog_heading my-3">Poll's of the Day</h1>
<div className="row">
{polls.reverse().map((poll, index) => (
<div className="col-lg-4 col-12 poll_border" key={index}>
<Poll
noStorage
question={poll.question}
answers={Object.keys(poll.options).map((key) => {
return {
option: key,
votes: poll.options[key].length,
};
})}
onVote={
(answer) =>
handalchange(poll._id, userId, answer, console.log(answer)) // getting vote
}
className="mb-2"
/>
</div>
))}
</div>
</div>
</div>
);
};
export default MainPoll;
this is my frontend-
POSTMAN - request = >
and here is my backend API -
// post
export const postPoll = (pollId, post) => {
return fetch(`${API}/vote/${pollId}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(post),
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
It depends on what object does onVote event from Poll component pass. But if it's vote object, that's required in postPoll method as second arguement, than:
function in onVote event should pass poll.id from this component and vote object from Vote component onVote event itself:
onVote={(vote) => handalchange(poll.id, vote)}
handalchange should fire postPoll api method with these arguements and load updated poll data on success:
const handalchange = (pollId, vote) => {
postPoll(pollId, vote).then(() => {
loadPoll();
});
}

External API issue in production - React.js

I build an app rendering data from an external API. In development mode everything works fine but in production the API doesn't load. It does't return anything not error in console.
axios.get("https://api.github.com/repos/....")
.then(response => ...)
.catch(error => console.log(error))
axios.get("https://api.github.com/...")
.then(response => {
....
})
.catch(error => console.log(error))
Can anyone tell me what the problem is?
You should check the network tab in the console and see which response code that request is returning. The catch block will only be hit if the response code of that request is one of these client errors listed on this website: https://httpstatuses.com/
Full code here
Note In development mode.
My signup form work fine, login work fine
But in production none of this work!
I have google during for one week but dont find answer.
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import ShowCase from './utils/table.github.user';
import Validator from "../mm-admin/auth/auth.validator"
const Homepage = () => {
const [organisations, setOrgnisations] = useState([])
const [searchContributor, setSearchContributor] = useState('');
const [searchContributorResult, setSearchContributorResult] = useState([]);
const [isAdded, setAdded] = useState(false);
useEffect(() => {
let cleanup = false;
requestApi()
return () => {
cleanup = true;
}
}, [searchContributor])
const requestApi = () =>{
axios.get("https://api.github.com/repos/git/git/contributors", {
params : {
rejectUnauthorized: false,//add when working with https sites
requestCert: false,//add when working with https sites
agent: false,//add when working with https sites
}
})
.then(response => {
const all = response.data;
const result = all.filter(contributor => {
return contributor.login.toLowerCase().includes(searchContributor.toLowerCase())
})
setSearchContributorResult(result)
})
.catch(error => console.log(error))
axios.get("https://api.github.com/organizations", {
params : {
rejectUnauthorized: false,//add when working with https sites
requestCert: false,//add when working with https sites
agent: false,//add when working with https sites
}
})
.then(response => {
const all = response.data;
const result = all.filter(contributor => {
return contributor.login.toLowerCase().includes(searchContributor.toLowerCase())
})
setOrgnisations(result)
})
.catch(error => console.log(error))
}
const makeSearchContr = event => {
event.preventDefault()
setSearchContributor(event.target.value)
}
const addFavorite = (favorite, notes) => event => {
event.preventDefault();
if (Validator.isAuthenticated()) {
const id = Validator.isAuthenticated().user._id;
const favorites = {
item : JSON.stringify(favorite),
note : notes
}
axios.put("/user/action/" + id, favorites)
.then(res => {
setAdded(true)
const timer = setTimeout(() => {
setAdded(false)
}, 4000)
return () => clearTimeout(timer)
})
.catch(error => console.log(error))
} else {
console.log("Need to loged")
}
}
const contributorGit = () => {
return searchContributorResult.map((contributor, index) => {
return <ShowCase key={index} item={contributor} status={isAdded} favorite={addFavorite}/>
})
}
const organisationsGit = () => {
return organisations.map((organisation, index) => {
return <ShowCase key={index} item={organisation} favorite={addFavorite}/>
})
}
return (
<article>
<div className="">
<div className="container">
<form>
<div className="">
</div>
<div className="form-group">
<input type="text" className="form-control" placeholder="Search" value={searchContributor} onChange={makeSearchContr}/>
</div>
</form>
</div>
</div>
<div className="github-user" id="github">
<div className="container">
<h2>List contributor :</h2>
<ul style={{paddingLeft : '0px'}}>
{contributorGit()}
</ul>
<h2>List organisation :</h2>
<ul style={{paddingLeft : '0px'}}>
{organisationsGit()}
</ul>
</div>
</div>
</article>
)
}
export default Homepage;

Why are my action creators being called in the wrong order when using thunk?

I was experimenting with redux-thunk and action creators and noticed some strange behavior that I don't understand. When I call the action creator functions they don't get called in the order I want them to. This is my App.js component
class App extends Component {
handleSave = () => {
this.props.postData({
name:this.props.activity,
type_name: this.props.type
})
this.props.fetchList()
this.props.fetchData()
}
handleClick = () => {
this.props.fetchData()
}
componentDidMount() {
this.props.fetchData()
this.props.fetchList()
}
render() {
return (
<div className="App">
<Router>
<Nav />
<Route exact path='/' render={props => <Home {...props} clickProp={this.handleClick} saveProp={this.handleSave}/>} />
<Route exact path='/activities' render={props => <ListContainer {...props} numItems={this.props.list.length} listProp={this.props.list}/>} />
</Router>
</div>
);
}
}
const mapStateToProps = state => {
return {
activity: state.activity,
type: state.type,
list: state.list
}
}
const actions = {fetchData, fetchList, postData}
export default connect(mapStateToProps, actions)(App);
When I click on a button in the Home child component the handleSave function gets called which is then supposed to post an item on to my list and then fetch the updated list so that it can be shown in my list. When I do this, this.props.fetchList() gets called first even though it is the second function being called. I have placed a console.log(action) inside my reducer and this is what is printed.
{type: "FETCH_LIST", payload: Array(86)}
{type: "POST_DATA", payload: {…}}
{type: "FETCH_DATA", payload: {…}}
The only way that I can get the FETCH_LIST to happen after the POST_DATA is if I call fetch_data() a second time like so
handleSave = () => {
// something weird going on here
this.props.fetchList()
this.props.postData({
name:this.props.activity,
type_name: this.props.type
})
this.props.fetchList()
this.props.fetchData()
}
I really want to be able to get my code to work without having to call the same function twice if it is possible. Finally, this is what my action creators look like.
export default function fetchData() {
return (dispatch) => {
const url = 'http://www.boredapi.com/api/activity/'
fetch(url)
.then(res => res.json())
.then(activity => { dispatch({type: "FETCH_DATA", payload: activity})})
}
}
export default function fetchList() {
return (dispatch) => {
const url = 'http://localhost:3001/activities'
fetch(url)
.then(res => res.json())
.then(list => { dispatch({type: "FETCH_LIST", payload: list})})
}
}
export default function postData(activity) {
return (dispatch) => {
const url = 'http://localhost:3001/activities'
const config = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({activity})
}
fetch(url, config)
.then(r => r.json())
.then(activity => {dispatch({type: "POST_DATA", payload: activity})})
}
}
My only guess is that this is happening because these actions are asynchronous. So I have also tried to change the order in which these functions are called and no matter what FETCH_LIST always happens before POST_DATA.
You can convert those action creators to async versions and then you can await them so they execute in order.
https://medium.com/#gaurav5430/async-await-with-redux-thunk-fff59d7be093
For example on your fetchData
function fetchData() {
return async (dispatch) => {
const url = 'http://www.boredapi.com/api/activity/'
try{
const res = await fetch(url)
const activity = await res.json();
dispatch({type: "FETCH_DATA", payload: activity})
}
catch (error) {
console.log(error);
}
}
Once they are async you can await them in your handleSave (once you convert it to async) This will ensure they get called in order.
handleSave = async () => {
await this.props.postData({
name:this.props.activity,
type_name: this.props.type
})
await this.props.fetchList()
await this.props.fetchData()
}

400 BAD REQUEST when POST using Axios in React

Can any help me with this?
I keep getting a 400 bad request from Axios.
I can pass a GET request and confirm its working fine.
I create http-common.js file with following code:
import axios from 'axios';
export default axios.create({
baseURL: 'https://5fa97367c9b4e90016e6a7ec.mockapi.io/api',
headers: {
'Content-type': 'application/json'
}
});
Then,I create a service that uses axios object above to send HTTP requests.
TodoService.js
import http from '../http-common/http-common';
const getAll=()=>{
return http.get('/todos');
};
const get=id=>{
return http.get(`/todos/${id}`);
};
const create=data=> {
return http.post('/todos',data);
};
const update=(id,data)=>{
return http.put(`/todos/${id}`,data);
};
const remove = id => {
return http.delete(`/todos/${id}`);
};
const removeAll = () => {
return http.delete(`/todos`);
};
const findByTitle = title => {
return http.get(`/todos?title=${title}`);
};
export default {getAll,get,create,update,remove,removeAll,findByTitle};
Then, I use TodoDataService.create(data) ... in AddTodos component.
AddTodos.js
import React, { useState } from 'react';
import TodoDataService from '../services/TodoService';
const AddTodos = () => {
const initialTodoState={
id:null,
title: '',
isDone: false,
user: ''
};
const [todo,setTodo]=useState(initialTodoState);
const [submitted,setSubmitted]=useState(false);
const handleInputChange=event=>{
const {name,value}=event.target;
setTodo({...todo,[name]:value});
};
const saveTodo =()=>{
var data={
title: todo.title,
isDone:todo.isDone,
user: todo.user
};
console.log(data);
TodoDataService.create(data)
.then(response => {
setTodo({
id:response.data.id,
title: response.data.title,
isDone: response.data.isDone,
user: response.data.user
});
setSubmitted(true);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const newTodo=()=>{
setTodo(initialTodoState);
setSubmitted(false);
};
return (
<div className="submit-form">
{submitted ? (
<div> //...
) : (
<div>
<div className="form-group"> //... </div>
<div className="form-group"> //... </div>
<button onClick={saveTodo} className="btn btn-success">
Submit
</button>
</div>
)}
</div>
)
}
export default AddTodos;
When clicked Submit it's giving this error:
I recreate your api call and got this response:
await fetch('https://5fa97367c9b4e90016e6a7ec.mockapi.io/api/todos', {
method: 'POST', body: JSON.stringify({id: "123",title: "homework", isDone: false, user: "foo"})})
.then(response => response.json())
.then(data => {
console.log(data)
})
error 400 "Max number of elements reached for this resource!"
you need to delete some records in order to insert new ones
so after deleting a record:
await fetch('https://5fa97367c9b4e90016e6a7ec.mockapi.io/api/todos/1', {
method: 'DELETE'})
.then(response => response.json())
.then(data => {
console.log(data)
})
VM623:5 {id: "1", title: "deneme", isDone: true, user: "cafererensimsek"}
and posting a new one, now it works

Categories

Resources