How can I POST data using API from REACTJS? - javascript

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();
});
}

Related

Search function now working in React photo gallary

Working on a small application that takes a pexels api and displays photos dynamically. When I send the search request for my api to fectch based on the new params, it does actually update the page with new photos but not the ones based on the params. I though I got the search function correct, maybe it's cause I'm not using it in a useEffect? But if I did use it in a useEffect, I wouldn't be able to set it on the onClick handle. I tried to console.log the query I was getting from the onChange but it doesn't seem like it's getting the result. What am I doing wrong?
import { useState, useEffect } from 'react'
import pexelsApi from './components/pexelsApi'
import './App.css'
const App = () => {
const [images, setImages] = useState([]);
const [loading, setLoading] = useState(false);
const [nextPage, setNextPage] = useState(1);
const [perPage, setPerPage] = useState(25);
const [query, setQuery] = useState('');
const [error, setError] = useState('');
useEffect(() => {
const getImages = async () => {
setLoading(true);
await pexelsApi.get(`/v1/curated?page=${nextPage}&per_page=${perPage}`)
.then(res => {
setImages([...images, ...res.data.photos]);
setLoading(false);
}).catch(er => {
if (er.response) {
const error = er.response.status === 404 ? 'Page not found' : 'Something wrong has happened';
setError(error);
setLoading(false);
console.log(error);
}
});
}
getImages();
}, [nextPage, perPage]);
const handleLoadMoreClick = () => setNextPage(nextPage + 1)
const search = async (query) => {
setLoading(true);
await pexelsApi.get(`/v1/search?query=${query}&per_page=${perPage}`)
.then(res => {
setImages([...res.data.photos]);
console.log(res.data)
setLoading(false);
console.log(query)
})
}
if (!images) {
return <div>Loading</div>
}
return (
<>
<div>
<input type='text' onChange={(event) => setQuery(event.target.value)} />
<button onClick={search}>Search</button>
</div>
<div className='image-grid'>
{images.map((image) => <img key={image.id} src={image.src.original} alt={image.alt} />)}
</div>
<div className='load'>
{nextPage && <button onClick={handleLoadMoreClick}>Load More Photos</button>}
</div>
</>
)
};
export default App
import axios from 'axios';
export default axios.create({
baseURL: `https://api.pexels.com`,
headers: {
Authorization: process.env.REACT_APP_API_KEY
}
});
Your main issue is that you've set query as an argument to your search function but never pass anything. You can just remove the arg to have it use the query state instead but you'll then need to handle pagination...
// Helper functions
const getCuratedImages = () =>
pexelsApi.get("/v1/curated", {
params: {
page: nextPage,
per_page: perPage
}
}).then(r => r.data.photos)
const getSearchImages = (page = nextPage) =>
pexelsApi.get("/v1/search", {
params: {
query,
page,
per_page: perPage
}
}).then(r => r.data.photos)
// initial render effect
useEffect(() => {
setLoading(true)
getCuratedImages().then(photos => {
setImages(photos)
setLoading(false)
})
}, [])
// search onClick handler
const search = async () => {
setNextPage(1)
setLoading(true)
setImages(await getSearchImages(1)) // directly load page 1
setLoading(false)
}
// handle pagination parameter changes
useEffect(() => {
// only action for subsequent pages
if (nextPage > 1) {
setLoading(true)
const promise = query
? getSearchImages()
: getCuratedImages()
promise.then(photos => {
setImages([...images, ...photos])
setLoading(false)
})
}
}, [ nextPage ])
The reason I'm passing in page = 1 in the search function is because the setNextPage(1) won't have completed for that first page load.

Need help to display data from external API

I fetched some data from an API, Im trying to display the data but I'm doing something wrong. Can someone help? I have attached a photos of the fetched data on the console and my code[![data api
import React, {useState, useEffect} from 'react'
import './Track.css';
export default function Track() {
const [carbon] = useState([])
useEffect( () => {
const headers = {
'Accept':'application/json'
};
fetch('https://api.carbonintensity.org.uk/intensity',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
})
return (
<div>
<p>Track</p>
<div>
<p>{carbon.forecast}</p>
</div>
</div>
)
}
]1]1
Change to
import React, { useState, useEffect } from 'react'
import './Track.css';
export default function Track() {
const [carbon, setCarbon] = useState([])
useEffect(() => {
const headers = {
'Accept': 'application/json'
};
fetch('https://api.carbonintensity.org.uk/intensity',
{
method: 'GET',
headers: headers
})
.then(function (res) {
setCarbon(res.data)
}).then(function (body) {
console.log(body);
});
})
return (
<div>
<div>
{carbon.map((obj, i) => (
<li key={i}>
<ul>{obj.from}</ul>
</li>
))}
</div>
</div>
)
}
I recommend to you study https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/map
you forgot some little thing:
first you forgot the setCarbon in the useStae hook you will need it to pass the response from the fetch.
You forgot to set the state in the fecth.
You will need to add a condition to render only when the state (carbon) is set.
you need to add an empty dependency to useEffect
import React, { useState, useEffect } from "react";
export default function Track() {
const [carbon, setCarbon] = useState([]);
useEffect(() => {
const headers = {
Accept: "application/json"
};
fetch("https://api.carbonintensity.org.uk/intensity", {
method: "GET",
headers: headers
})
.then((res) => {
return res.json();
})
.then((body) => {
console.log(body.data);
setCarbon(body.data);
});
}, []);
return (
<div>
<p>Track</p>
{carbon.length > 0 && (
<div>
{carbon.map((c, i) => (
<p key={i}>
<div>from: {c.from} </div>
<div>to: {c.to}</div>
<div>forecast: {c.intensity.forecast}</div>
<div>actual: {c.intensity.actual}</div>
<div>index: {c.intensity.index}</div>
</p>
))}
</div>
)}
</div>
);
}
Here you go,
Remember, state is like a place to store data for your component.
When you use fetch, you are getting data and now you need to save it to your state.
If you use state inside of your JSX, you can get the information to display.
Check out the console log, to look at the data structure that is returned from the fetch. This is what is set to the state "data". It can be called whatever you want. You can iterate through it, and dynamically display the data in JSX if you wanted, but I just hardcoded it for you so it's easier to understand.
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://api.carbonintensity.org.uk/intensity", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
.then((res) => res.json())
.then((data) => setData(data))
.catch((e) => console.error(e));
}, []);
console.log("data:", data);
return (
<div>
<p>Track</p>
<div>
<p>From: {data.data["0"].from}</p>
<p>To: {data.data["0"].to}</p>
<div>Intensity:</div>
<p>forecast: {data.data["0"].intensity.forecast}</p>
<p>forecast: {data.data["0"].intensity.actual}</p>
<p>forecast: {data.data["0"].intensity.index}</p>
</div>
</div>
);

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.

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

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.'
});
}
});
});

React hooks array passing in number when passed into props of component

I am currently working on a chat application and for some reason every time I pass in my array of messages as a prop to another component it passes in a number to the component instead of the message object. I have tried a lot of different methods of passing it in regarding using multiple components etc but it seems to still be passing in the number of elements for some reason. Any help is appreciated... code is below
Component receiving the props
import React, { useEffect } from 'react'
import Message from '../../Message/Message'
function Messages({ messages }) {
useEffect(() => {
console.log(messages)
}, [messages])
return (
<div>
test
</div>
)
}
export default Messages
// Import React dependencies.
import React, { useEffect, useState, } from "react";
// Import React dependencies.
import io from 'socket.io-client'
import axios from 'axios'
import Messages from './Messages/Messages'
import uuid from 'react-uuid'
import { Redirect } from 'react-router-dom'
// Import the Slate components and React plugin.
const ENDPOINT = 'http://localhost:5000/'
export const socket = io.connect(ENDPOINT)
const LiveChatFunction = ({ group_id }) => {
// Add the initial value when setting up our state.
const [message, setValue] = useState("")
const [user, setUser] = useState("")
const [groupId, setGroup] = useState('')
const [messages, setMessages] = useState([])
const [toLogin, userAuth] = useState(false)
useEffect(() => {
setGroup(group_id)
axios.post('http://localhost:5000/api/users/refresh_token', null, { withCredentials: true }).then(data => {
if (!data.data.accessToken) {
userAuth(true)
}
})
axios.get('http://localhost:5000/api/users/userInfo', { withCredentials: true }).then(data => {
setUser(data.data.user)
})
socket.on(`message-${group_id}`, data => {
setMessages(messages.push(data))
});
axios.get(`http://localhost:5000/live/${group_id}`).then(x => {
console.log(x.data)
})
}, [group_id, messages])
function setClick() {
const data = {
messageId: uuid(),
user,
groupId,
message
}
socket.emit('message', data)
}
if (toLogin) {
return (
<Redirect to="/login" />
)
}
return (
<div>
<input placeholder="message" type="text" onChange={value => {
setValue(value.target.value)
socket.emit('typing-message', { username: user, time: new Date() })
}} />
<button onClick={setClick}>Submit</button>
<Messages messages={messages} />
</div>
)
}
export default LiveChatFunction;
I have added some comments of what I think you can change:
useEffect(() => {
const recieveFunction = (data) => {
//using callback so no dependency on messages
setMessages((messages) => messages.push(data));
};
async function init() {
//next line is pointless, this runs when group_id
// has changed so something must have set it
// setGroup(group_id);
await axios //not sure if this should be done before listening to socket
.post(
'http://localhost:5000/api/users/refresh_token',
null,
{ withCredentials: true }
)
.then((data) => {
if (!data.data.accessToken) {
userAuth(true);
}
});
await axios
.get('http://localhost:5000/api/users/userInfo', {
withCredentials: true,
})
.then((data) => {
setUser(data.data.user);
});
//start listening to socket after user info is set
socket.on(`message-${group_id}`, recieveFunction);
axios
.get(`http://localhost:5000/live/${group_id}`)
.then((x) => {
console.log(x.data);
});
}
init();
//returning cleanup function, guessing socket.off exists
return () =>
socket.off(`message-${group_id}`, recieveFunction);
}, [group_id]); //no messages dependencies
console.log('messages are now:',messages);
If messages is still not set correctly then can you log it
So I think I found your problem:
In your useEffect hook, you're setting messages to the wrong thing.
socket.on(`message-${group_id}`, data => {
setMessages(messages.push(data))
});
An example:
const m = [].push();
console.log(m);
// m === 0
const n = [].push({});
console.log(n);
// n === 1
As you can see this is the index.
So what you need is:
socket.on(`message-${group_id}`, data => {
messages.push(data);
setMessages(messages);
});
This will set messages to the array of messages.

Categories

Resources