Conditional Routing in React based on API calls - reactjs

So I'm trying to create a React web app with multiple pages and connecting it to Flask to fetch data using the fetch API. Here is what I want to achieve:
If the user submits a Form, React does a POST request to the Flask API which returns a JSON object, which is received by React and I render the predict route. This is handled using the Forms.jsx component, which has the following code:
const Form = () => {
const [title, setTitle] = useState("");
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
const movie_submit = {title};
console.log(movie_submit);
fetch('/predict', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(movie_submit)
}).then(() => {
(navigate("/predict"));
})
}
return (
<div className='form_container'>
<form className='form' onSubmit={handleSubmit}>
<input type='text' placeholder='Movie Name' autoFocus
autoComplete='off' value={title} onChange={(e)=>setTitle(e.target.value)}/>
<button className='button'>Recommend!</button>
</form>
</div>
)
}
export default Form
Now I want to perform a GET request to the Flask API to get what should be put into the Predict.js page (/predict route), and the show it.
Predict.js is as:
const Predict = () => {
const [movies, setMovies] = useState([]);
useEffect(() => {
fetch('/predict').then(response =>
response.json().then(data =>
{
setMovies(Object.values(data));
}))
}, []);
const movie_name = movies.map((movie) => <p key={movie.toString()}>{movie}</p>);
return (
<div>
<Navbar />
<h1>Predictions</h1>
<br />
<h2><Movies movie={movie_name}/></h2>
</div>
)
}
export default Predict
But I want this to be such that if the user hasn't submitted the form, then it navigates to /apology route, and if the FLASK API GET request returns an empty object, even then it navigates to /apology route. How do I do this? I understand this is conditional routing of some sort, but I havent been able to quite achieve where I should do this. Here <Movies /> is simply a component that helps in showing the movie names

You can pass a data to the state prop of the location object.
fetch('/predict', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(movie_submit)
}).then(() => {
(navigate('/predict', { state: { wasFetched: true } }));
})
then in your Predict Component:
const { state } = useLocation();
const { wasFetched } = state;
useEffect(() => {
if (wasFetched) {
// user submited the form
} else {
// user hasn't submited the form
}
}, [wasFetched]);

Related

onClick event doesn't work on first click - React

I'm a react newbie. Though, I've resolved the issue, just curious to know why it happens, why onClick sends an empty object to backend on first click. Along with sending the data to database I had to update a field on UI, thus, set a state to do the job. and what is the standard practice in these situations. I mean when I have to update the db as well as show the update instantly
const [item,setItem] = useState([]);
const handleAddProducts = e =>{
e.preventDefault();
const productName = e.target.productName.value;
const price = parseFloat(e.target.price.value);
const quantity = parseInt(e.target.quantity.value);
const image = e.target.image.value;
const data = {productName,price,quantity,image};
const url = 'http://localhost:5000/add';
if(data.productName && data.price && data.quantity && data.image){
fetch(url,{
method:'POST',
headers:{
'content-type':'application/json'
},
body:JSON.stringify(item)
})
.then(res=>res.json())
.then(result=>{
if(result.insertedId){
toast('Product Added Successfully');
e.target.reset();
}
})
}
else{
toast("Nothing to Upload");
}
}
I think your handleAddProducts is getting called on an onFormSubmit event, please add a full code snippet so we can help you better.
I'll assume your handleAddProducts function is getting called on a form submit
const handleAddProducts = (e) => {
e.preventDefault()
let { productName, price, quantity, image } = new FormData(e.target)
price = parseFloat(price)
quantity = parseInt(quantity)
const url = 'http://localhost:5000/add'
if (productName && price && quantity && image) {
fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(item),
})
.then((res) => res.json())
.then((result) => {
if (result.insertedId) {
toast('Product Added Successfully')
e.target.reset()
}
})
} else {
toast('Nothing to Upload')
}
}
e.target when a form gets submitted doesn't include the inputs, it holds information about the <form> HTML element itself, and there's no way you can read the inner form values except by using something such as new FormData(e.target).
Talking about how usually React Devs manage these kinds of stuff, this is called inputs state management.
Basically, you keep recording every change that occurs to an input element in a separate state.
ex:
const myComponent = () => {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [phoneNum, setPhoneNum] = useState('')
return (
<form onSubmit={handleSubmit}>
<input onChange={({ target }) => setFirstName(target.value)} value={firstName} />
<input onChange={({ target }) => setLastName(target.value)} value={lastName} />
<input onChange={({ target }) => setPhoneNum(target.value)} value={phoneNum} />
</form>
)
}

Why the flag is showed every time I reload the page after client has been created?

What I have done by far is when a user creates a client, in the top right of the page, is shown a flag(notification), which says "Client has been successfully created".
To do that was a little complex for me, because saving the client to DB, and listing the client to the web page are in two different components. Also, the flag is another component as well.
To save and list the clients I have used Axios since I'm dealing with the backend a lot.
SaveClient.js
export default function SaveClient({}) {
const save = async () => {
const clientParams = {
userName:
currentClient: clientName,
clientId: clientId,
};
await axios
.post(
process.env.REACT_API_CLIENT, clientParams
)
.then((response) => {
navigate("/clientlist", {state: {showFlagCreate: true}}); //passing the state
})
.catch((error) => {;
console.log(error);
});
};
}
ClientList.js
export default function ClientList() {
const { state } = useLocation();
const showFlagCreate = state?.showFlagCreate;
const [clientlist, setClientList] = useState([])
useEffect(() => {
const clientParams = {
userName:
currentClient: clientName,
clientId: clientId,
};
axios
.get(process.env.REACT_API_CLIENT, clientParams)
.then((response) => {
const {data} = response
setClientList(data)
})
.catch((error) => console.log(error));
}, []);
return (
<div>
...
{showFlagCreate && <FlagCreateClient />}
</div>
);
}
FlagCreateClient
export default function FlagCreateClient() {
const [show, setShow] = useState(true);
return (
<div>
<Transition
show={show}
as={Fragment}
<div>
<p>The client is successfully created.</p>
</div>
<div>
<button onClick={() => {setShow(false)}}>
<span>Close</span>
</button>
</div>
</Transition>
<div/>
);
}
The idea is that in the SaveClient component, when a client is saved, in .then() inside the navigate() function, pass a state in a true condition.
Then in the ClinetList component, I call the state using useLocation(), and I passed in the component {showFlagCreate && <FlagCreateClient />}.
By far this logic works, I create a client, the flag is shown after, but when I reload the page after, the flag is shown. When I make other actions in the webpage which might be clicking the edit button and going back to the ClientList component the flag won't show, even if I reload/refresh the page.
How can I fix this bug?

Unable to update state variables after fetch in ReactJS

On form submit, a new event is added in the Database and the details of this new event are returned back via JSON. I want to concatenate this new JSON in the 'events' data so that the 'events' data gets updated automatically. But it is not working. Here is my code. Please see the line commented as //Here. It is where I am trying to update the event state variable but it is not getting updated.
import React, { useState,useEffect } from 'react';
async function getAllEvents() {
return fetch('http://localhost:8080/get-all-events/', {
method: 'GET',
//body: JSON.stringify(credentials)
})
.then(
data => data.json()
)
}
export default function Dashboard() {
const [events, setEvents ] = useState({});
const [eventName, setEventName] = useState();
useEffect(() => {
getEventsWithFetch();
}, []);
const getEventsWithFetch = async () => {
const response = await fetch("http://localhost:8080/get-all-events/");
const jsonData = await response.json();
setEvents(jsonData);
};
const addEvent = async (name) => {
const response = await fetch("http://localhost:8080/create-event/",
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"title": name,
"day": "1",
"month": "2"
})
}
).then((response) => response.json())
.then((json) => {
events.push(json);
var events_array = events;
setEvents(events_array); //Here
})
};
const handleSubmit = async e => {
e.preventDefault();
var data = new FormData(e.target)
}
return(
<div>
<p>EVENTS DASHBOARD</p>
{JSON.stringify(events)}
<form onSubmit={handleSubmit}>
<label>
<p><b>Event Name</b></p>
<input type="text" name="event_name" onChange={e => setEventName(e.target.value)} />
</label>
<div>
<button type="submit">Submit</button>
</div>
</form>
</div>
);
}
You're modifying the read-only state events, instead of using setEvents, which is already a mistake.
Furthermore, the diff-check React does is by reference for objects, so you have to clone events, instead of setting the state to itself:
setEvents([...events, json]); // Using spread to clone the array and add a new element in the end
Otherwise, React sees the same reference being set and ignores it.

How to post data into my React application?

I have an issue with my react, I'm working on a MERN template but I can't make my post to work, and I want to be able to add a new blog on my site. When I add a new blog, I seem to get it in my console.log. (the title and the description) but not on my app, I believe it's something with my fetch.
this is my app.js file
import React, {useEffect, useState} from 'react';
import {Router} from "#reach/router";
import Blogs from "./Blogs";
import Blog from "./Blog";
const API_URL = process.env.REACT_APP_API;
function App() {
const [blog, setBlogs] = useState([]);
const [postCount, setPostCount] = useState(0);
useEffect(() => {
async function getData() {
const url = `${API_URL}/blogs`;
const response = await fetch(url);
const data = await response.json();
setBlogs(data);
}
getData();
}, [postCount]);
function getBlog(id) {
const blogObject = blog.find(data => data._id === id);
return blogObject;
}
//callback så min addBlog ved hvor den skal hente data fra
async function addBlog(title, description, date) {
console.log("title", title);
console.log("Description" , description);
const newBlog = {
title: title,
description: description,
date: date
}
const url = `${API_URL}/blogs`;
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json'
},
body: JSON.stringify(newBlog),
});
const data = await response.json();
//setBlogs([...blogs, newBlog]);
setPostCount(postCount + 1); //call my post count that fecths my data automatic
console.log("blog", data);
}
return (
<>
<h1>Blog App!</h1>
<Router>
<Blogs path="/" blogs={blog} addBlog={addBlog}>{blog.id}</Blogs>
<Blog path="/blogs/:id" getBlog={getBlog}></Blog>
</Router>
</>
);
}
export default App;
this is my addBlog.js
import React, { useState } from 'react';
function AddBlog(props) {
//state const for hver properties i din object(question)
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [date, setDate] = useState("");
return (
<><label>Title: </label>
<input type="text" placeholder="Write the title of your Blog" size="30" onChange={(event) => {
setTitle(event.target.value)
}
} /><br /><label>Description: </label>
<input type="text" placeholder="Write the description..." size="30" onChange={(event) => {
setDescription(event.target.value)
}} />
<br />
<button onClick={(event) => props.addBlog(title, description, date)}>Add Question</button>
</>
);
}
export default AddBlog;
I hope someone is able to help me out.
UPDATE here's my screendump of my console - when I press add blog it says POST 401 unAuthrorized.
SORRY IT WAS THE WRONG PROJECT I POSTED AN IMAGE BUT NOW IT'S THE RIGHT PROJECT
Screendump of my console
After looking at your logs, I think you need to send authorization headers alongside your fetch request in order for the back-end to respond.
You could add the authorization header like that - however, you need to find out/generate authorization token that your backend can accept. Also a little improvement, I would make the function a bit more dynamic by allowing it to accept an URL.
useEffect(() => {
async function getData(url) {
const response = await fetch(URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': <your auth token>
}
});
const data = await response.json();
setBlogs(data);
}
getData(`${API_URL}/blogs`);
}, [postCount]);

How to add object attribute using useState (react hook) from different state that is set by children?

I'm new to React and I hope it is not a duplicate question here, somewhere and sorry if my explanation is wrong. So, I got this 'createEvent' panel that has 3 children's; name, start date (comes from a calendar component), end date (also) and details that come from the input text area. I created 'createEvent' panel as a parent that contains states for every data that come from the child. Inside the parent component, I want to create an object that can be sent to the backend. I tried to get data from children and using useState() to give values to the object attributes. If I try to get data using input from parent it works, but not getting input from children and I don't know what to do.
I`ll leave here examples of one child, create Event panel and post request. If I figure out how to do with these examples, I'll know to do it forward.
insert name component
import React, { useState } from 'react';
import './title.style.css';
export default function Title(props) {
const [name, setName] = useState(props.name);
function handlerName(e) {
props.handlerName(e.target.value);
}
return(
<div class="md-form active-cyan active-cyan-2 mb-3">
<input
class="form-control"
type="text"
placeholder={name}
aria-label="Name"
onChange={(e) => {handlerName(e)}}
/>
</div>
);
}
create event panel
import React, { useState, useEffect } from 'react';
import './createEvent.style.css';
import Name from '../input/title.component.js';
import Calendar from '../input/calendar.component.js';
import Details from '../input/details.component.js';
import CardService from '../../service/CardService.js';
export default function CreateEvent() {
const [eventName, setEventName] = useState("");
const [eventStartDate, setEventStartDate] = useState(new Date());
const [eventEndDate, setEventEndDate] = useState(new Date());
const [eventDetails, setEventDetails] = useState("");
const [eventObject, setEventObject] = useState({
"name": "",
"startDate": new Date(),
"endDate": new Date(),
"details": ""
});
async function handleName(name1){
await setEventName(name1);
}
async function handleStartDate(date){
await setEventObject({startDate: date});
}
async function handleEndDate(date){
await setEventObject({endDate: date});
}
async function handleDetails(value){
await setEventObject({details: value});
}
function handleSubmit(e) {
CardService.postEvent(eventObject)
}
return(
<form onSubmit={(e) => handleSubmit(e)}>
<div className="Create-event">
<div>
<Name name={'Insert event name'} handlerName={handleName} />
</div>
<div className="date">
<Calendar type="submit" handlerDate={handleStartDate} date={eventStartDate}/>
</div>
<div className="date">
<Calendar type="submit" handlerDate={handleEndDate} date={eventEndDate}/>
</div>
<div className="text">
<Details type="submit" handleDetails={handleDetails}/>
</div>
<button className="createButton" >
Create
</button>
</div>
</form>
);
}
post request
const CardService = {
postEvent(value1){
fetch("http://localhost:8080/event/ReminderApp/api/v1/event/" ,
{
method: 'POST',
headers: {
"Content-Type" : "application/JSON" ,
},
dataType: "json",
body: JSON.stringify(value1),
})
.then(response => response.json())
.then(json => {console.log(json)
})
.catch(err => {
console.log('Type send failed', err);
});
},
getEvents() {
return fetch('http://localhost:8080/event/ReminderApp/api/v1/event')
.then((response) => response.json())
.then((response) => {
return response;
});
},
deleteEvent(value) {
fetch("http://localhost:8080/event/ReminderApp/api/v1/event/" + value,
{
method: 'DELETE',
})
.catch(err => {
console.log('Type send failed', err);
});
},
};
export default CardService;
I tried to set all createEvent states in handle function and after using useEffect to set all fields from the object, but the app got broken. I tried to setEventObject() inside the handleSubmit function and call CardService.postEvent() but it sent the default object.
What I show you now, it is the last version of my attemps.

Resources