Data not send correctly - javascript

import './App.css';
import OutputBox from './OutputBox';
import React, { useState } from 'react';
import * as functions from "./functions.js"
import axios from 'axios'
function App() {
//define state variables
const [cargo, setCargo] = useState('');
const [acceleration, setAcceleration] = useState('');
const [takeoff_time, setTakeOffTime] = useState('');
const [takeoff_distance, setTakeOffDistance] = useState('');
const [excess_cargo, setExcessCargo] = useState('');
const [date, setDate] = useState();
const [message, setMessage] = useState('');
//send the data to /insert-data in order to store it in the db
const insertData = () => {
axios.post("http://localhost:3001/insert-data", {
takeoff_time: takeoff_time.toString(),
takeoff_distance: takeoff_distance.toString(),
excess_cargo: excess_cargo.toString(),
cargo: cargo.toString()
}).then(()=> {
console.log("success");
}).catch((error) => {
console.error(error);
});
console.log(takeoff_time +" | "+ takeoff_distance+" | "+excess_cargo+" | "+cargo);
};
//get the cargo data
//return if the input is valid
const validInput = (cargo) => {
if (cargo < 0 || isNaN(cargo)) {
alert("invalid input!");
return false;
}
else {
return true;
}
}
//A function that centralizes the actions
const handleData = () => {
if(validInput(cargo)){
//update the variables every time it change
setAcceleration(functions.getAcceleration(cargo));
setTakeOffTime(functions.getTakeOffTime(cargo));
setTakeOffDistance(functions.getTakeOffDistance(cargo));
setExcessCargo(functions.getExcessCargo(cargo));
//call the insert function
insertData();
}
}
//asynchronous func to handle the date data
const handleDate = async () => {
try { //make a post request to send date data to the back-end
const response = await axios.post("http://localhost:3001/date-picker", { date });
setMessage(response.data); //set the changed value to the current variable
}catch(error){ //trying to catch errors
console.log(error);
}
}
//prints to the web the data
return (
<div className='App'>
<div className='data'>
<h1>Shimshon physics calculator</h1>
<label>Enter cargo weight:</label>
<input type="number" onChange={ (event) => {setCargo(event.target.valueAsNumber)}}/>
<button onClick={handleData}>Send data</button>
<OutputBox text={`Acceleration: ${acceleration}`} />
<OutputBox text={`Takeoff Time: ${takeoff_time}`} />
<OutputBox text={`Takeoff Distance: ${takeoff_distance}`} />
<OutputBox text={`Excess Cargo: ${excess_cargo}`} />
<br />
<label>Enter flight date:</label>
<input type= "date" onChange={(event) => { setDate(event.target.value) }} />
<button onClick={handleDate}>Send date</button>
<OutputBox text={`you ${ message } able to filght`} />
</div>
</div>
);
}
export default App;
the problem is when I enter cargo and click send data.
I receive in the table in the db only the entered cargo value, and the other values are empty.
But when I re-enter input for cargo and click on send data I get in the table of the db the value of the current entered cargo and the previous value of (takeoff_time, takeoff_distance, excess_cargo).
any solution pls??

The reason your app behaves like it does has to do with the way states are updated. When calling insertData you update 4 states before calling the axios function. States update asynchronously. That means that your app won't wait for the states to have changed before running the rest of your code. So in practice, insertData will be called before any of the states have been modified, leaving you with empty values.
The solution lies in the way that you handle changed data. Instead of updating the four states when clicking the button, update them immediately after the cargo state has changed.
I'd recommend that you change the acceleration, take off, distance and excess cargo states to into useMemo hooks. This way you'll immediately make the calculations necessary for any future requests without triggering a re-render.
The snippet below is an example of you would implement these practices. I've also added multiple useCallback hooks. Both useMemo and useCallback will store the result based on the states they're dependant on so that the same calculation doesn't have be to done more than once. This is not mandatory, but an optimization.
import './App.css';
import OutputBox from './OutputBox';
import React, { useState, useMemo, useCallback } from 'react';
import axios from 'axios'
import {
getAcceleration,
getTakeOffTime,
getTakeOffDistance,
getExcessCargo
} from "./functions.js"
function App() {
const [cargo, setCargo] = useState(0);
const [date, setDate] = useState();
const [message, setMessage] = useState('');
const isValidCargo = useMemo(() =>
cargo >= 0 && !isNaN(cargo)
}, [cargo]);
const acceleration = useMemo(() =>
getAcceleration(cargo),
[cargo]);
const takeoffTime = useMemo(() =>
getTakeOffTime(cargo),
[cargo]);
const takeoffDistance = useMemo(() =>
getTakeOffDistance(cargo),
[cargo]);
const excessCargo = useMemo(() =>
getExcessCargo(cargo),
[cargo]);
const insertData = useCallback(() => {
axios.post("http://localhost:3001/insert-data", {
takeoff_time: takeoffTime.toString(),
takeoff_distance: takeoffDistance.toString(),
excess_cargo: excessCargo.toString(),
cargo: cargo.toString()
}).then(()=> {
console.log("success");
}).catch((error) => {
console.error(error);
});
}, [takeoffTime, takeoffDistance, excessCargo, cargo]);
const handleData = useCallback(() => {
if (isValidCargo) {
insertData();
}
}, [isValidCargo]);
const handleDate = useCallback(async () => {
try {
const response = await axios.post("http://localhost:3001/date-picker", {
date
});
setMessage(response.data);
} catch (error) {
console.log(error);
}
}, [date]);
return (
<div className='App'>
{/* The rest of your rendered body here */}
</div>
);
}

Related

TypeError: Cannot destructure property 'company' of 'jobs[value]' as it is undefined

I am using useEffect and useState hooks to fetch data and destructure it. But I'm getting this error every time.
Here is the code.
import React, { useState, useEffect } from 'react';
import { FaAngleDoubleRight } from 'react-icons/fa';
import Jobs from './Jobs';
// ATTENTION!!!!!!!!!!
// I SWITCHED TO PERMANENT DOMAIN
const url = 'https://course-api.com/react-tabs-project';
function App() {
const [loading, setLoading] = useState(true);
const [jobs, setJobs] = useState([]);
const [value, setValue] = useState(0);
const fetchJobs = async () => {
const response = await fetch(url);
const newJobs = await response.json();
setJobs(newJobs);
setLoading(false);
// console.log(newJobs);
};
useEffect(() => {
fetchJobs();
}, []);
const{company, dates, duties, title}=jobs[value];
console.log(jobs[value]);
// const { company, dates, duties, title } = jobs[value];
return (
<section className='section '>
<div className='title'>
<h2>experience</h2>
<div className='underline'></div>
</div>
{/* <Jobs jobs={jobs} /> */}
</section>
);
}
export default App;
Error image
If I comment out the destructuring, I get the value 6 times. The First 2 times it is undefined.
browser console
You are destructuring properties from the object when still the data is not fetched and the array length is 0
import React, { useState, useEffect } from "react";
import { FaAngleDoubleRight } from "react-icons/fa";
import Jobs from "./Jobs";
// ATTENTION!!!!!!!!!!
// I SWITCHED TO PERMANENT DOMAIN
const url = "https://course-api.com/react-tabs-project";
function App() {
const [loading, setLoading] = useState(true);
const [jobs, setJobs] = useState([]);
const [value, setValue] = useState(0);
const [currentJob, setCurrentJob] = useState();
const fetchJobs = async () => {
const response = await fetch(url);
const newJobs = await response.json();
setJobs(newJobs);
setLoading(false);
if (newJobs.length > 0) setCurrentJob(newJobs[value]);
// console.log(newJobs);
};
useEffect(() => {
fetchJobs();
}, []);
// const{company, dates, duties, title}=jobs[value];
// console.log(jobs[value]);
if (loading) return <h2>Loading...</h2>;
return (
<section className="section ">
<div className="title">
<h2>experience</h2>
<div className="underline"></div>
</div>
{/* <Jobs jobs={jobs} /> */}
</section>
);
}
export default App;
I have added another state variable currentJob which will assume the job item based on value variable when successfully the fetch is completed, although I would suggest to use the jobs array directly based on your component requirements.

Displaying an image from an API In React

I am trying to display the image of the breed selected by the user in this code but it is not working,
any ideas or hints as of why?
Thank you
import React, { useState, useEffect } from 'react';
import './breed-image.css';
function BreedImage () {
const [breed, selectedBreed] = useState('');
useEffect(() => {
fetchImage();
}, []);
const fetchImage = async () => {
const res = await fetch(`https://dog.ceo/api/breed/${breed}/images/random`)
const data = await res.json();
const imageUrl = data.message
selectedBreed(imageUrl);
};
return (
<div className="image-container">
<img className="image-card" src={breed} alt="doggopicture" />
</div>
);
}
export default BreedImage;
There's some weird logic in here that doesn't make sense. breed is initialized to an empty string. Then, in your useEffect you have an empty dependencies array, which means it will be called once. Which means your API request hits https://dog.ceo/api/breed//images/random which presumably would fail (since breed was '').
Most likely you instead want:
import React, { useState, useEffect } from 'react';
import './breed-image.css';
function BreedImage () {
const [breed, setBreed] = useState('');
const [url, setUrl] = useState('');
useEffect(() => {
fetchImage();
}, [breed]);
const fetchImage = async () => {
const res = await fetch(`https://dog.ceo/api/breed/${breed}/images/random`)
const data = await res.json();
setUrl(data.message);
};
return (
<>
<DogPicker onChange={(breed) => setBreed(breed)} />
<div className="image-container">
<img className="image-card" src={url} alt="doggopicture" />
</div>
</>
);
}
export default BreedImage;
where you'd pass setBreed to some other component. Or, you could pass breed down to this component as a prop, and again use useEffect to watch for changes.

don't understand how can I get pollId from reactjs poll

this is my react code here I am getting react poll using API but when I start working on handalchange For POST API request I need (PollId,userId and answer) I am getting userId through { const userId = isAutheticated() && isAutheticated().user._id; } but I do not understand how can I get PollId from my all polls, please help...!
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 { useParams } from "react-router-dom";
const MainPoll = () => {
const userId = isAutheticated() && isAutheticated().user._id;
const pollId = useParams();
const id = pollId._Id;
console.log(id);
const [polls, setPoll] = useState([]);
const [error, seterror] = useState(false);
// Setting answers to state to reload the component with each vote
const [pollAnswers, setPollAnswers] = useState([]);
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 = () => {
postPoll();
console.log("hello");
};
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={handalchange}
className="mb-2"
/>
</div>
))}
</div>
</div>
</div>
);
};
export default MainPoll;
my frontend image -
Here I have 5 polls , so I can not get PollId from useParams ...! so how can I get..?
Your component seems to represent list of polls, not any specific poll. So if you have an array of polls instead of one poll, than you have multiple ids instead of the single one.
You can get them by mapping your polls array like that:
const pollIds = polls.map((poll) => poll.id); // or any other prop that stores id

wait 500 miliseconds before running react hook

I have built this custom react hook :
import { useEffect, useContext, useState } from 'react';
import { ProductContext } from '../contexts';
import axios from 'axios';
export default function useProducts(searchQuery) {
const [products, setProducts] = useContext(ProductContext);
const [isLoading, setIsloading] = useState(true);
useEffect(() => {
axios
.get(`/shoes?q=${searchQuery ? searchQuery : ''}`)
.then((res) => {
setProducts(res.data);
setIsloading(false);
})
.catch((err) => {
console.error(err);
});
}, [searchQuery, setProducts]);
return { products, isLoading };
}
It basically fetches some data based on a query string that i pass in. The query string comes from an input field :
import React, { useState } from 'react';
import { FiSearch } from 'react-icons/fi';
import { useProducts } from '../../hooks';
export default function SearchBar() {
const [query, setQuery] = useState('');
const handleChange = (e) => {
e.preventDefault();
setQuery(e.target.value);
};
useProducts(query);
return (
<div className="search-form">
<FiSearch className="search-form__icon" />
<input
type="text"
className="search-form__input"
placeholder="Search for brands or shoes..."
onChange={handleChange}
/>
</div>
);
}
The problem is it will fetch while the user is typing. I want it to fetch after the user didnt type for 500 miliseconds.
What I tried is :
setTimeout(() => {
useProducts(query);
}, 500);
But this will return an error saying :
src\components\header\SearchBar.js
Line 14:5: React Hook "useProducts" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
Search for the keywords to learn more about each error.
You can debounce your value with an additional piece of state. Once query is changed, we set off a 500 ms timer that will set the value of debounced. However, if the effect re-runs, we clear that timer and set a new timer.
import React, { useState, useEffect } from 'react';
import { FiSearch } from 'react-icons/fi';
import { useProducts } from '../../hooks';
export default function SearchBar() {
const [query, setQuery] = useState('');
const [debounced, setDebounced] = useState('');
useEffect(() => {
const timeout = setTimeout(() => {
setDebounced(query);
}, 500);
return () => { clearTimeout(timeout) }
}, [query])
const handleChange = (e) => {
e.preventDefault();
setQuery(e.target.value);
};
useProducts(debounced);
return (
<div className="search-form">
<FiSearch className="search-form__icon" />
<input
type="text"
className="search-form__input"
placeholder="Search for brands or shoes..."
onChange={handleChange}
/>
</div>
);
}
I'd change the useProducts hook to accept a debounce time as a parameter, and have it make the axios call only once the debounce time is up:
useProducts(query, 500);
export default function useProducts(searchQuery, debounceTime = 0) {
const [products, setProducts] = useContext(ProductContext);
const [isLoading, setIsloading] = useState(true);
const [timeoutId, setTimeoutId] = useState();
useEffect(() => {
clearTimeout(timeoutId);
setTimeoutId(setTimeout(() => {
axios
.get(`/shoes?q=${searchQuery ? searchQuery : ''}`)
.then((res) => {
setProducts(res.data);
setIsloading(false);
})
.catch((err) => {
console.error(err);
});
}, debounceTime));
}, [searchQuery, setProducts]);
return { products, isLoading };
}

Using React useEffect hook with rxjs mergeMap operator

I'm trying to implement a data stream that has to use inner observables, where I use one from mergeMap, concatMap etc.
e.g.:
const output$$ = input$$.pipe(
mergeMap(str => of(str).pipe(delay(10))),
share()
);
output$$.subscribe(console.log);
This works fine when logging into console.
But when I try to use it in React like below utilizing useEffect and useState hooks to update some text:
function App() {
const input$ = new Subject<string>();
const input$$ = input$.pipe(share());
const output$$ = input$$.pipe(
mergeMap(str => of(str).pipe(delay(10))),
share()
);
output$$.subscribe(console.log);
// This works
const [input, setInput] = useState("");
const [output, setOutput] = useState("");
useEffect(() => {
const subscription = input$$.subscribe(setInput);
return () => {
subscription.unsubscribe();
};
}, [input$$]);
useEffect(() => {
const subscription = output$$.subscribe(setOutput);
// This doesn't
return () => {
subscription.unsubscribe();
};
}, [output$$]);
return (
<div className="App">
<input
onChange={event => input$.next(event.target.value)}
value={input}
/>
<p>{output}</p>
</div>
);
}
it starts acting weird/unpredictable (e.g.: sometimes the text is updated in the middle of typing, sometimes it doesn't update at all).
Things I have noticed:
If the inner observable completes immediately/is a promise that
resolves immediately, it works fine.
If we print to console instead of useEffect, it works fine.
I believe this has to do something with the inner workings of useEffect and how it captures and notices outside changes, but cannot get it working.
Any help is much appreciated.
Minimal reproduction of the case:
https://codesandbox.io/s/hooks-and-observables-1-7ygd8
I'm not quite sure what you're trying to achieve, but I found a number of problems which hopefully the following code fixes:
function App() {
// Create these observables only once.
const [input$] = useState(() => new Subject<string>());
const [input$$] = useState(() => input$.pipe(share()));
const [output$$] = useState(() => input$$.pipe(
mergeMap(str => of(str).pipe(delay(10))),
share()
));
const [input, setInput] = useState("");
const [output, setOutput] = useState("");
// Create the subscription to input$$ on component mount, not on every render.
useEffect(() => {
const subscription = input$$.subscribe(setInput);
return () => {
subscription.unsubscribe();
};
}, []);
// Create the subscription to output$$ on component mount, not on every render.
useEffect(() => {
const subscription = output$$.subscribe(setOutput);
return () => {
subscription.unsubscribe();
};
}, []);
return (
<div className="App">
<input
onChange={event => input$.next(event.target.value)}
value={input}
/>
<p>{output}</p>
</div>
);
}
I had a similar task but the goal was to pipe and debounce the input test and execute ajax call.
The simple answer that you should init RxJS subject with arrow function in the react hook 'useState' in order to init subject once per init.
Then you should useEffect with empty array [] in order to create a pipe once on component init.
import React, { useEffect, useState } from "react";
import { ajax } from "rxjs/ajax";
import { debounceTime, delay, takeUntil } from "rxjs/operators";
import { Subject } from "rxjs/internal/Subject";
const App = () => {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [filterChangedSubject] = useState(() => {
// Arrow function is used to init Singleton Subject. (in a scope of a current component)
return new Subject<string>();
});
useEffect(() => {
// Effect that will be initialized once on a react component init.
// Define your pipe here.
const subscription = filterChangedSubject
.pipe(debounceTime(200))
.subscribe((filter) => {
if (!filter) {
setLoading(false);
setItems([]);
return;
}
ajax(`https://swapi.dev/api/people?search=${filter}`)
.pipe(
// current running ajax is canceled on filter change.
takeUntil(filterChangedSubject)
)
.subscribe(
(results) => {
// Set items will cause render:
setItems(results.response.results);
},
() => {
setLoading(false);
},
() => {
setLoading(false);
}
);
});
return () => {
// On Component destroy. notify takeUntil to unsubscribe from current running ajax request
filterChangedSubject.next("");
// unsubscribe filter change listener
subscription.unsubscribe();
};
}, []);
const onFilterChange = (e) => {
// Notify subject about the filter change
filterChangedSubject.next(e.target.value);
};
return (
<div>
Cards
{loading && <div>Loading...</div>}
<input onChange={onFilterChange}></input>
{items && items.map((item, index) => <div key={index}>{item.name}</div>)}
</div>
);
};
export default App;

Categories