Validation in react - reactjs

I need your help. I'm trying to validate the input as well as do a search. I take data from JSON placeholder. I have the functionality that you can get the whole list, and you can get it separately, by id.
There are no errors in the console, but not everything works. When I try to get lists of posts, photos, albums, and posts, I do not see anything, but I wrote the logic of drawing elements to each of the endpoints. What is my mistake? Thank you very much
import React, {useState} from "react";
export let Inputs = () => {
const Base_Url = 'https://jsonplaceholder.typicode.com'
const availableResources = ['posts', 'comments', 'photos', 'albums', 'todos', 'users']
const [endPoint, setEndpoint] = useState('');
const [id, setId] = useState('');
const [items, setItems] = useState([]);
const [singleItem, setSingleItem] = useState(null);
const [error, setError] = useState('');
const onSubmit = () => {
if (!endPoint){
return setError('First Input Is Required')
}
if (!availableResources.includes(endPoint)) {
return setError('Value is not valid')
}
const idToNub = Number(id);
if (!idToNub && id !== '') return
fetchData()
setError('')
}
const fetchData = async () => {
const response = await fetch(`${Base_Url}/${endPoint.trim()}/${id.trim()}`)
const data = await response.json()
if (id) {
setSingleItem(data)
setItems([])
return
}
setSingleItem(null)
setItems(data)
}
return (
<div>
<input
type="text"
placeholder="Type posts, comments, todos"
value={endPoint}
onChange={({target : {value}}) => setEndpoint(value)}
/>
<input
type="text"
placeholder="Type id number"
value={id}
onChange={({target : {value}}) => setId(value)}
/>
<button onClick={onSubmit}>Fetch Data</button>
<pre>{singleItem && JSON.stringify(singleItem, null)}</pre>
<h1>{error}</h1>
{items.map(el => <div key={el.id}>{el?.body}</div>)
&& items.map(el => <div key={el.id}>{el?.title}</div>)
&& items.map(el => <div key={el.id}>{el?.name}</div>)}
</div>
)
}

Just iterate through your items once and check the values of each item:
import React, {useState} from "react";
export let Inputs = () => {
const Base_Url = 'https://jsonplaceholder.typicode.com'
const availableResources = ['posts', 'comments', 'photos', 'albums', 'todos', 'users']
const [endPoint, setEndpoint] = useState('');
const [id, setId] = useState('');
const [items, setItems] = useState([]);
const [singleItem, setSingleItem] = useState(null);
const [error, setError] = useState('');
const onSubmit = () => {
if (!endPoint){
return setError('First Input Is Required')
}
if (!availableResources.includes(endPoint)) {
return setError('Value is not valid')
}
const idToNub = Number(id);
if (!idToNub && id !== '') return
fetchData()
setError('')
}
const fetchData = async () => {
const response = await fetch(`${Base_Url}/${endPoint.trim()}/${id.trim()}`)
const data = await response.json()
if (id) {
setSingleItem(data)
setItems([])
return
}
setSingleItem(null)
setItems(data)
}
return (
<div>
<input
type="text"
placeholder="Type posts, comments, todos"
value={endPoint}
onChange={({target : {value}}) => setEndpoint(value)}
/>
<input
type="text"
placeholder="Type id number"
value={id}
onChange={({target : {value}}) => setId(value)}
/>
<button onClick={onSubmit}>Fetch Data</button>
<pre>{singleItem && JSON.stringify(singleItem, null)}</pre>
<h1>{error}</h1>
{items.map(el => {
const fields = Object.keys(el);
let renderItems = fields.map(field => <div key={el[field]}>{el[field]}</div>)
return renderItems;
})
}
</div>
)
}

Related

useEffect run useNavigate when visiting the page

I'm new to React, and I'm trying to make a recpie app with react, right know I want to save the data in json file from the add form. so I can save the data but when I want to redirect the user to the home page using useEffict with navigate. I can't go to the create page when adding navigate to the useEffict.
Create file code:
import { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useFetch } from "../../hooks/useFetch";
// Styles
import "./Create.css";
export default function Create() {
const [title, setTitle] = useState("");
const [method, setMethod] = useState("");
const [cookingTime, setCookingTime] = useState("");
const [newIngredient, setNewIngredient] = useState("");
const [ingredients, setIngredients] = useState([]);
const { postData, data } = useFetch("http://localhost:3000/recipes", "POST");
const ingredientsInput = useRef(null);
const navigate = useNavigate();
// Methods
const handleSubmit = (e) => {
e.preventDefault();
postData({
title,
ingredients,
method,
cookingTime: cookingTime + " minutes",
});
};
const handleAdd = (e) => {
e.preventDefault();
const ing = newIngredient.trim();
if (ing && !ingredients.includes(ing)) {
setIngredients((preIng) => [...preIng, ing]);
}
setNewIngredient("");
ingredientsInput.current.focus();
};
useEffect(() => {
if (data) {
navigate("/");
console.log(data);
}
}, [data, navigate]);
return (
<div className="create">
<form onSubmit={handleSubmit}>
<label>
<span>Recipe Title:</span>
<input
type="text"
onChange={(e) => setTitle(e.target.value)}
value={title}
required
/>
</label>
<label>
<span>Recipe ingredients:</span>
<div className="ingredients">
<input
type="text"
onChange={(e) => setNewIngredient(e.target.value)}
value={newIngredient}
ref={ingredientsInput}
/>
<button onClick={handleAdd} className="btn">
Add
</button>
</div>
</label>
{ingredients.length > -1 && (
<p>
Current ingredients:{" "}
{ingredients.map((ing) => (
<span key={ing}>{ing}, </span>
))}
</p>
)}
<label>
<span>Recipe Method:</span>
<textarea
onChange={(e) => setMethod(e.target.value)}
value={method}
required
/>
</label>
<label>
<span>Recipe Time (minutes):</span>
<input
type="number"
onChange={(e) => setCookingTime(e.target.value)}
value={cookingTime}
required
/>
</label>
<button className="btn">Submit</button>
</form>
</div>
);
}
useFetch file code:
import { useState, useEffect } from "react";
export const useFetch = (url, method = "GET") => {
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const [option, setOption] = useState(null);
const postData = (data) => {
setOption({
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
};
useEffect(() => {
const controller = new AbortController();
const fetchData = async (fetchOption) => {
setIsPending(true);
try {
const res = await fetch(url, {
...fetchOption,
signal: controller.signal,
});
if (!res.ok) {
throw new Error(res.statusText);
}
const data = await res.json();
setIsPending(false);
setData(data);
setError(null);
} catch (err) {
if (err.name === "AbortError") {
console.log("the fetch was aborted");
} else {
setIsPending(false);
setError("Could not fetch the data");
}
}
};
if (method === "GET") {
fetchData();
}
if (method === "POST") {
fetchData(option);
}
return () => {
controller.abort();
};
}, [url, option, method]);
return { data, isPending, error, postData };
};
I don't know from where the issue came.
The problem was from useFetch file. when I want to do a post request I shoud cheack if the option useState has a value.
Before I was just check if there is a post method:
const [option, setOptions] = useState(null);
if (method === "POST") {
fetchData(option);
}
Know I'm checking if there is a value in option
const [option, setOptions] = useState(null);
if (method === "POST" && option) {
fetchData(option);
}
You basically trying to add a variable that is not a react state variable into the useEffect on update
const [recipes, setReceipies] = useState();
useEffect(async ()=> { const {data} = awawit useFetch("http://localhost:3000/recipes", "POST")
setReceipies(data);
},[])
navigate("/");
},[recipes]);
Or ofc you can navigate all the way from the mounting useEffect
Good Luck
after you save the data, simply add this code
const history = createBrowserHistory()
history.push(`/`)
I have big apps, that use history, and I never had a problem with it.
and I recomend you to use SWR for data-fetching - React Hooks for Data Fetching.
very simple and powerfull tool:
https://swr.vercel.app/

OnChange Event Stuck in Loop, Crashing

I am getting an infinite loop / crash here.
I'm trying to get an onChange event fired for these radio buttons (which are built after pulling data from a query), but I think it keeps redrawing and I can't figure out why.
Any thoughts on how I can solve this?
const GetChallenge = async () => {
const slug = useParams()
const data = await shopifyApolloClient.query({ query: singleProduct(slug) })
return data
}
const Challenge = () => {
let [loaded, setLoaded] = useState(false)
let [product, setProduct] = useState([])
let [variants, setVariants] = useState([])
let [options, setOption] = useState()
let [metafields, setMetafields] = useState([])
GetChallenge().then((ret) => {
setProduct(ret.data.product)
setVariants(ret.data.product.variants.edges)
setOption(ret.data.product.variants.edges[0].node.title)
setMetafields(ret.data.product.metafields.edges)
setLoaded(true)
})
const handleOptions = (event) => {
setOption(event.target.value)
}
if (loaded === true) {
return (
<div>
{variants.map((e) => (
<label
htmlFor={e.node.title}
key={e.node.id}>
<input
type="radio"
name="options"
checked={e.node.title === options}
value={e.node.title}
onChange={handleOptions}
/>
{e.node.title}
</label>
))}
</div>
)
} else {
return (
<p>Not Loaded</p>
)
}
}
GetChallenge is triggering every render. Try useEffect with the empty array empty soas to trigger only onmount.
import React, { useState, useEffect } from 'react';
const GetChallenge = async () => {
...
useEffect(() => {
GetChallenge().then((ret) => {
setProduct(ret.data.product)
setVariants(ret.data.product.variants.edges)
setOption(ret.data.product.variants.edges[0].node.title)
setMetafields(ret.data.product.metafields.edges)
setLoaded(true)
}),[]}
...
}
Try this:
onChange={(event) => setOption(event.target.value)}
hope this will solve your problem :)
import React, { useState, useEffect } from 'react';
const GetChallenge = async () => {
const slug = useParams();
const data = await shopifyApolloClient.query({ query: singleProduct(slug) });
return data;
};
const Challenge = () => {
const [data, setData] = useState({
loaded: false,
product: [],
variants: [],
options: "",
metafields: []
});
const { loaded, variants, options } = data;
useEffect(() => {
GetChallenge().then((ret) => {
setData((prevState) => ({
...prevState,
product: ret.data.product,
variants: ret.data.product.variants.edges,
options: ret.data.product.variants.edges[0].node.title,
metafields: ret.data.product.metafields.edges,
loaded: true
}));
});
}, []);
const handleOptions = (event) => {
setData((prevState) => ({ ...prevState, options: event.target.value }));
};
if (loaded === true) {
return (
<div>
{variants.map((e) => (
<label htmlFor={e.node.title} key={e.node.id}>
<input
type="radio"
name="options"
checked={e.node.title === options}
value={e.node.title}
onChange={(event) => handleOptions(event)}
/>
{e.node.title}
</label>
))}
</div>
);
} else {
return <p>Not Loaded</p>;
}
};

How to set user input to be capitalised

I am trying to access the https://docs.openaq.org/ api and I have created a search function, where you can type the name of the city which should set a state which then is added to the url used to the data. However the user input must be capitalised and I can't seem to get it to work.
import React, {useState} from 'react';
import Axios from 'axios';
import Button from '../UI/Button';
import SearchedCity from './SearchedCity';
const Search = () => {
const [searchedCity, setSearchedCity] = useState('');
const [city, setCity] = useState();
const [airQuality, setAirQuality] = useState();
const [dateAndTime, setDateAndTime] = useState();
const [latitude, setLatitude] = useState();
const [longitude, setLongitude] = useState();
const [country, setCountry] = useState();
const searchCity = async () => {
try{
const url = `https://api.openaq.org/v1/measurements?country=GB&city=${searchedCity}`;
const res = await Axios.get(url);
console.log(res)
} catch (error) {
alert('Please learn to spell');
}
}
const handleSubmit = (e) => {
e.preventDefault();
searchCity();
console.log({searchedCity})
}
const handleChange = (e) => {
let userInput = e.target.value;
userInput.charAt(0).toUpperCase()
console.log(userInput)
setSearchedCity(userInput)
}
return (
<div>
<form onSubmit={handleSubmit} className="form" >
<label>
<input type="text" placeholder="Search for a UK city" onChange={handleChange} />
<Button handleSubmit={handleSubmit}></Button>
</label>
</form>
<SearchedCity city={city} ></SearchedCity>
</div>
);
};
export default Search;
Try something like this
userInput = userInput.charAt(0).toUpperCase() + userInput.slice(1);
It will capitalise the first letter but keep the rest of the string same.
I think the issue is that you don't set the value after changing that character.
So, just do that.
const handleChange = (e) => {
let userInput = e.target.value;
userInput = userInput.charAt(0).toUpperCase()
console.log(userInput)
setSearchedCity(userInput)
}

Trigger react useEffect

I'm fetching data from a firebase db it works when the component renders, but I can't make it to fetch again when there is a new entry in my db.
What I've tried
I've tried passing a state to the dependency array of useEffect and I changed that state every time my form was submitted (That's the time when there's a new entry in my db)
App
function App() {
const [showForm, setShowForm] = useState(true);
const [tasks, setTasks] = useState([]);
const [isSubmitted, setIsSubmitted] = useState(true);
//Fetch tasks from server
const fetchData = () => {
fetch(
"https://react-task-tracker-8e519-default-rtdb.firebaseio.com/tasks.json"
)
.then((response) => {
return response.json();
})
.then((data) => {
const tasks = [];
//Convert the data to an array so i can map over it
for (const key in data) {
const task = {
id: key,
...data[key],
};
tasks.push(task);
}
setTasks(tasks);
});
};
useEffect(() => {
fetchData();
}, [isSubmitted]);
//Show/Hide form
const onAddHandler = () => {
setShowForm(!showForm);
};
const formSubmitted = () => {
setIsSubmitted(!isSubmitted);
console.log(isSubmitted);
};
return (
<Container>
<Header click={onAddHandler} isShown={showForm}></Header>
{showForm ? <Form fs={formSubmitted}></Form> : ""}
<Tasks tasks={tasks}></Tasks>
</Container>
);
}
export default App;
Form
function Form(props) {
const [task, setTask] = useState();
const [dayTime, setDayTime] = useState();
const [reminder, setReminder] = useState();
//Posting Form data to firebase (DUMMY API)
const postFormData = (fullTask) => {
fetch(
"https://react-task-tracker-8e519-default-rtdb.firebaseio.com/tasks.json",
{
method: "POST",
body: JSON.stringify(fullTask),
headers: {
"Content-Type": "application/json",
},
}
);
};
//Make an object of form data
const onSubmit = (e) => {
e.preventDefault();
const fullTask = {
task: task,
dayTime: dayTime,
reminder: reminder,
};
//Post func call
postFormData(fullTask);
props.fs();
//Field clearing
setTask("");
setDayTime("");
setReminder("");
};
return (
<AddForm onSubmit={onSubmit}>
<FormControl>
<Label>Task</Label>
<Input
type="text"
placeholder="Add Task"
onChange={(e) => setTask(e.target.value)}
value={task}
required
></Input>
</FormControl>
<FormControl>
<Label>Day & Time</Label>
<Input
type="text"
placeholder="Add Task"
onChange={(e) => setDayTime(e.target.value)}
value={dayTime}
required
></Input>
</FormControl>
<FromControlCheck>
<CheckLabel>Set Reminder</CheckLabel>
<CheckInput
type="checkbox"
onChange={(e) => setReminder(e.currentTarget.checked)}
value={reminder}
></CheckInput>
</FromControlCheck>
<Submit type="submit" value="Save Task"></Submit>
</AddForm>
);
}
export default Form;
I would pass fetchData as a props to <Form>. When submitted, I would call it.
Form
const onSubmit = async (e) => {
e.preventDefault();
const fullTask = {
task: task,
dayTime: dayTime,
reminder: reminder,
};
//Post func call
await postFormData(fullTask);
await props.fetchData();
//Field clearing
setTask("");
setDayTime("");
setReminder("");
};
Then remove the isSubmitted state.
Try change the "Id" value to "id". Try make it the same name as the key for the id in "fecthData" function.
I think this solve your problem
function App() {
const [showForm, setShowForm] = useState(true);
const [tasks, setTasks] = useState([]);
const [isSubmitted, setIsSubmitted] = useState(false);
//Fetch tasks from server
const fetchData = () => {
fetch(
"https://react-task-tracker-8e519-default-rtdb.firebaseio.com/tasks.json"
)
.then((response) => {
return response.json();
})
.then((data) => {
const tasks = [];
//Convert the data to an array so i can map over it
for (const key in data) {
const task = {
id: key,
...data[key],
};
tasks.push(task);
}
setTasks(tasks);
});
};
useEffect(() => {
if (isSubmitted) {
fetchData();
setIsSubmitted(false);
}
}, [isSubmitted]);
//Show/Hide form
const onAddHandler = () => {
setShowForm(!showForm);
};
const formSubmitted = () => {
setIsSubmitted(true);
console.log(isSubmitted);
};
return (
<Container>
<Header click={onAddHandler} isShown={showForm}></Header>
{showForm ? <Form fs={formSubmitted}></Form> : ""}
<Tasks tasks={tasks}></Tasks>
</Container>
);
}
export default App;

set Function not working in custom hook with useEffect

I'm working on a custom hook that relies on an async operation in useEffect. I cannot get my set function to actually set the value of the result of the async operation. In this case, country is always null in my App component so nothing is ever rendered. foundCountry gets set correctly, but setCountry doesn't seem to work. Thanks for the help!
const useCountry = name => {
const [country, setCountry] = useState([null]);
useEffect(() => {
const findCountry = async () => {
const foundCountry = await axios.get(
`https://restcountries.eu/rest/v2/name/${name}?fullText=true`
);
setCountry(foundCountry);
};
if (name !== '') findCountry();
}, [name]);
};
And here is my App component where I am using the custom hook
const App = () => {
const nameInput = useField('text');
const [name, setName] = useState('');
const country = useCountry(name);
const fetch = e => {
e.preventDefault();
setName(nameInput.value);
};
return (
<div>
<form onSubmit={fetch}>
<input {...nameInput} />
<button>find</button>
</form>
<Country country={country} />
</div>
);
};
You defined the custom hook, but you forgot to return the country state as the result:
const useCountry = name => {
const [country, setCountry] = useState([null]);
useEffect(() => {
const findCountry = async () => {
const foundCountry = await axios.get(
`https://restcountries.eu/rest/v2/name/${name}?fullText=true`
);
setCountry(foundCountry);
};
if (name !== '') findCountry();
}, [name]);
// you forgot to return it
return country;
};
You can try this
const useCountry = name => {
const foundCountry = await axios.get(
`https://restcountries.eu/rest/v2/name/${name}?fullText=true`
);
if (name !== '') return findCountry();
return;
};
//App container
const [country, setCountry] = useState('');
useEffect(() => {
setCountry(useCountry(name))
}, [name])

Resources