How to refetch properly an API request after an event occurred? - javascript

i'm a react beginner and I'm struggling with the refresh of the components.
In this case, I have a GET request to an API triggering in a useEffect hook with no dependencies on my App.js. Of course, this is working just fine when my app just starts. However, I need it to trigger again after certain events ocurre, like an Onclick event in a button, and a OnSubmit event in a form, in order to re render the updated table component where the API data is displayed.
What is the best way to do this?
I thought about just calling the fetch function again whenever I need to update the data, but I don't know if that's the way to go.
Then, I currently have this workaround function to force the reload of the entire page after the events trigger, but i'm pretty sure this is not proper either:
const refresh = () =>{
window.location.reload(false);
}
I've been also tweaked the dependencies in the use effect (where the fetch happens) a little bit, because I'm feeling like the solution is there, but I had only bad results, like infinite loops.
Finally guys, I'll post an overview pic of my App code. Have in mind that I need to do the refresh in the next components: OrdenPago y Grid.
import { useState, useEffect } from 'react';
import { OrdenPago } from './OrdenDePago';
import EstadoCvus from './EstadoCvus';
import Grid from './Grid';
import axios from 'axios';
export const App = () => {
const [ops, setOps] = useState({ ordenesPago: [] });
useEffect( () => {
axios.get("http://cpawautojava:1400/consultaOrdenesPago")
.then(response => {
setOps(response.data);
console.log(response.data);
}).catch((error) => { console.log(error) })
}, [])
return (
<div className="container">
<div className='row'>
<div className='col-12'>
<div className='text-white py-2' style={{ backgroundColor: "#414BB2", height: "40px" }} >
<h1 className='text-center lead fw-bold'>Gestión de OPs</h1>
</div>
</div>
</div>
<div className='row mt-3' >
<div className='col-6 d-flex justify-content-center'>
<OrdenPago />
</div>
<div className='col-6 form1' >
<EstadoCvus />
</div>
</div>
<div className='row'>
<div className='col-12' >
<Grid ops={ops} />
</div>
</div>
</div>
);
}
Thanks in advance for your help.
Rodrigo.

Related

Location/city value is undefined after passing through other component and then render page gone blank..any solution?

I have two components "search" and "Maindata". I am passing the input value from the search component to maindata component where I want to replace the city attribute with the input value(location) in API. but the browser display went blank and the console give an undefined 'city' error, etc. I got stuck in this problem if anyone has a solution?
Here "search" component;
import React , {useState} from "react";
import Maindata from "./Maindata";
import "../Componentstyle/search.css";
export default function Search() {
const [location, setLocation] = useState();
<Maindata city={location}/>
return (
<div className="main">
<nav className="istclass">
<form className="form">
<div className="search">
<input
value={location}
placeholder="search city"
className="searchbox"
onChange={(e)=>setLocation(e.target.value)}
/>
<button className="nd" onClick={(e)=>setLocation(e.target.value)}>
Submit
</button>
</div>
</form>
</nav>
</div>
);
}
Here "Maindata" component;
import React, { useState, useEffect } from "react";
import "../Componentstyle/Main.css";
export default function Maindata(props) {
const [data, setData] = useState(null);
let city = console.log(props.city);
let weather = async () => {
const key = "1ab6ef20384db1d7d9d205d609f7eef0";
await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}&units=metric&formatted=0`
)
.then((response) => response.json())
.then((actualData) => setData(actualData));
};
useEffect(() => {
weather();
}, []);
if (!data) {
return <div>Loading...</div>;
}
const link = `http://openweathermap.org/img/w/${data.weather[0].icon}.png`;
return (
<div className="maindata">
<div className="city">{data.name}</div>
<div className="temp">{data.main.temp} C</div>
<div className="icon">
<img src={link} alt="not found" />{" "}
</div>
<div className="feel">feels Like {data.main.feels_like} C</div>
<div className="wind">Wind {data.wind.speed} Km/hr</div>
<div className="cloudy">{data.weather[0].main}</div>
<div className="humidity">humidity {data.main.humidity}%</div>
<div className="sunrise">
sunrise :- {new Date(data.sys.sunrise * 1000).toUTCString()}{" "}
</div>
<div className="sunset">
sunset :- {new Date(data.sys.sunset * 1000).toUTCString()}
</div>
</div>
);
}
<Maindata city={location}/>
keep this line of code inside the return
In your example, there is no meaningful connection between the Search and Maindata components. Meaning Maindata component will not get rendered on the page because it is not in the return statement of the Search component.
The Maindata component as below, is in JSX format, when you use JSX in your code in React, under the hood, React.createElement() method is being called.
Each call to React.createElement returns an object describing what to render to that part of the page. So it makes sense to put the Maindata component in the return statement. That is responsible for rendering the HTML elements from that component when you're loading a page containing that component.
<Maindata city={location}/> // is JSX and should be in the return statement to get rendered on the page and showing the right location

Maintaining checkbox value on local Storage React

I am trying to save the value of checkboxes in local storage, so when the user reloads the page, they remain checked/unchecked.
I have an "isChecked" state and a handleOnChange function.
What im trying to do is store the value on the state, and every time the onChange fuction runs, i set the 'checkbox' key on local storage to the value of the checkbox.
this is what my component looks like
import React from "react";
import { useState, useEffect } from "react";
const Pdf = (props) => {
const [shown, setShown] = useState(false);
const [isChecked, setIsChecked] = useState(localStorage.getItem('checkbox') === 'true');
const handleOnChange = (e) => {
setIsChecked(localStorage.getItem('checkbox'));
localStorage.setItem('checkbox',`${e.target.checked}`)
}
const toggle = () => {
setShown((prevState) => !prevState);
alert(isChecked)
};
return (
<div className="pdfContainer row container justify-content-center">
<section className="justify-content-center">
<h1 className="row justify-content-center h1--style">
Κεφάλαιο {props.id}
</h1>
<p className="row justify-content-center p--style">{props.info}</p>
</section>
<button onClick={toggle} className=" shadow button--style">
Μάθημα {props.id}
</button>
{shown && <embed className=" pdf" src={props.pdf} />}
<div className="pdfChecked input-group-text input-group ">
<input
type="checkbox"
id='pdfchecked'
name="pdfChecked"
value='1'
checked={isChecked}
onChange={handleOnChange}
/> <label className="input--label"> Διάβασα το κεφάλαιο!</label>
</div>
</div>
);
};
export default Pdf;
I understand that this is very buggy, and very much not how you're supposed to do it. I found the local storage concept hard to grasp as a beginner, and i have tried many ways to make this work. As of now, it kinda works, meaning when i reload the page it stays the same, but i have to click on the checkbox multiple times! SO buggy. Any help, especially letting me know why my thought process is wrong, would be appreciated!

Conditional render of props in react

I have this 2 states, from 2 different api calls
one is 'movieList' and the other one is 'search', Both are array of movies.
movieList is automatically rendered since it is the search for popular movies and it corresponds that the user is shown as soon as he opens the page, in the navbar I have an input attached to a state called search and it saves an array of movies that match the name in it ... try to use a conditional with the following logic, if search exists, maps search, otherwise map movieList.
but it seems I don't know how to do it correctly. If someone can give me a hand in how to do it, it would help me a lot, thank you very much! here I leave the code
import { useSelector } from 'react-redux'
import { getAllMovies } from '../../features/movieSlice'
import MovieCard from '../MovieCard/MovieCard';
const MovieListing = ({ movieList, search }) => {
return (
<div className='' >
<div className=''>
<div className='my-20 mx-15 flex flex-wrap justify-around items-center' >
{
movieList.map((movie)=>(
<MovieCard {...movie} key={movie.id} />
))}
</div>
</div>
</div>
)
}
export default MovieListing```
To do a conditional render you can try use a ternary operator:
<div className='' >
<div className=''>
<div className='my-20 mx-15 flex flex-wrap justify-around items-center' >
{
search ? search.map((searchItem) => <MapsSearch {...search}/> :
movieList.map((movie) => (<MovieCard {...movie} key={movie.id}/>))
}
</div>
</div>
</div>
(You'll just need to modify the mapping of search and what it returns as I'm not sure what type search is)

ReactJS: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state

Hello iam newbie in ReactJS, I'm using a library react-bootstrap4-form-validation to validate my form. From the documentation there are the function that use to reset the form validation. From the documentations there are example to reset the form validation using triggering button onClick, like this. And for a condition, i need to reset the validation, but not when triggering button onClick, but when the props from parent component is change. Because of these needs I also created a function (useCompare) that is used to compare the props received from the parent component.
In short, I want to reset my form, when the props received from the parent component changes, and the code is as shown below.
import React, { useRef } from "react";
import { ValidationForm, TextInput } from 'react-bootstrap4-form-validation';
import { useCompare } from '../../services/compare-props';
function TestForm({ form }) {
const formRefs = useRef();
const handleSubmit = (e, formData, inputs) => {
e.preventDefault();
console.log(formData)
}
if ( !useCompare(form) ) {
resetForm()
}
function resetForm() {
let formRef = formRefs.current;
formRef != null ? formRef.resetValidationState(true) : null;
}
return (
<div className="row justify-content-center">
<div className="col-md-6 col-sm-10">
<div className="shadow-sm p-3 mb-5 bg-white rounded border">
<h6>{form.labelForm}</h6>
<hr />
<ValidationForm onSubmit={handleSubmit} id="form-test" ref={formRefs}>
{form.field.map(function (fields, index) {
return (
<div className="row form-group mb-1" key={index}>
<div className="col-lg-4 col-sm-4 col-md-4">{fields.label}</div>
<div className="col-lg-8 col-sm-8 col-md-8">
<TextInput
type={fields.type}
className="form-control"
name={fields.name}
autoComplete="off"
required
{...(form.formType == 'add' && fields.name == 'brand_code' ? {minLength : "4"} : {})}
/>
</div>
</div>
);
})}
<button type="submit" className="btn btn-danger">
Save
</button>
<button type="button" className="btn btn-warning" onClick={() => resetForm()}>
Reset Form
</button>
</ValidationForm>
</div>
</div>
</div>
);
}
export default TestForm;
The code above works fine when the props received from the parent component doesn't change, and when I try to reset the form via onClick trigger button, it works fine too. But when I want to reset the form when the props of the parent component changes, it generates an error like this:
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state
Can anyone help me solve this problem? I am very grateful beforehand.
Try moving useCompare check to a side effect:
useEffect( () => {
if ( !useCompare(form) ) {
resetForm()
}
}, [useCompare, form, resetForm] )
You will likely have to wrap resetForm in a useCallback hook.
This useEffect will run every time form changes, and placing it here should prevent the 'update during render' issue.

Trying to render Firestore information in React component - utilization of useRef() not re-rendering and useState() raises error

I am currently working with the following React component:
function Dashboard(props) {
const loadingDash = useRef(true);
const dataDash = useRef([]);
// var [loading, setLoading] = useState(true)
// var [data, setData] = useState([])
// Style info goes here - unimportant to question at hand
var userName,
userEmail,
planRank,
designRank,
implementRank,
testRank,
maintRank;
useEffect(() => {
db.collection('Users')
.doc(props.user)
.onSnapshot({ includeMetadataChanges: true }, function (userInfo) {
dataDash.current = userInfo.data();
console.log('dataDash: ', dataDash);
if (dataDash.current !== undefined) {
loadingDash.current = false;
console.log('Loading dash: ', loadingDash);
}
});
});
if (loadingDash) {
return <h1>We're watching, and we're waiting</h1>; // Activation spinner here
}
return (
<div className={classes.app}>
<NavBar loggedIn={props.isLoggedIn} />
<div className={classes.page}>
<img className={classes.photo} src={defaultprofile} alt={'UserPhoto'} />
<h1 className={classes.profilename}>UserName</h1>
<h4 className={classes.profileinfo}>Email: email</h4>
<h4 className={classes.profileinfo}>Rank: "Rank"</h4>
</div>
<div className={classes.page}>
<h1 className={classes.progressname}>Learning Progress</h1>
<p className={classes.progressinfotop}>Planning - 75%</p>
<LinearProgress
variant="determinate"
className={classes.progressbar}
value={75}
/>
<p className={classes.progressinfo}>Design - 50%</p>
<LinearProgress
variant="determinate"
className={classes.progressbar}
value={50}
/>
<p className={classes.progressinfo}>Implementation - 100%</p>
<LinearProgress
variant="determinate"
className={classes.progressbar}
value={100}
/>
<p className={classes.progressinfo}>Testing & Deployment - 75%</p>
<LinearProgress
variant="determinate"
className={classes.progressbar}
value={75}
/>
<p className={classes.progressinfo}>Maintenance - 25%</p>
<LinearProgress
variant="determinate"
className={classes.progressbarbottom}
value={25}
/>
</div>
<div className={classes.page}>
<p className={classes.continuetext}>
Want to continue where you left off, click below to continue!
</p>
<button className={classes.buttons}>
<Link to="/LearnMore" className={classes.buttonlinks}>
Continue
</Link>
</button>
</div>
<div className={classes.footer}>
Copyright: Allison Broski, Shelby McKay, Maurice Fuentes, Timothy
Carpenter, Tanner Porteous
<p>Oakland University</p>
</div>
</div>
);
}
export default Dashboard;
My basic problem is that I am trying to retrieve information from a Firestore database, and then display individual attributes onto the page. However, I've run into the issue of fetching the data asynchronously. In an attempt to fix this, I referenced this and one which suggested using React state. This is what my component is based of. However, when I used the useState() feature in React, it raised the following error:
Assignments to the 'loading' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect.
In an attempt to address the error, I changed it to utilize useRef() functionality. However, now the component won't re-render when loading changes, because according to the documentation this isn't a feature of useRef(). Every other tutorial I have looked at says to utilize useState() but I can't apparently do that.
Are there any suggestions on ways to address these issues, such that I can accurately render data from a Firestore database?
The loading variable should be in the state (useState). I think that the error message shows because there are no dependencies in the useEffect call so basically every change re-render the whole component on every prop change and indeed, it will override the render value.
I believe that you want to run the listener only once the component first render. In this case, you should pass an empty array as second argument (the dependencies)
useEffect(() => {
db.collection('Users').doc(props.user).onSnapshot({
includeMetadataChanges: true
},
function(userInfo) {
dataDash.current = userInfo.data()
console.log('dataDash: ', dataDash)
if (dataDash.current !== undefined) {
setLoading(false);
}
})
}, []); // <-- here

Categories

Resources