How can I manipulate the search input provided by user in react? - reactjs

I am building an application that fetches a player's details, using the input. But the api only allows fetching the details using player's id, hence I have to use another method to first get the id using player's name. But there is some problem getting the input. I also tried using e.target.value, but it isn't working
import React, { useEffect, useState } from 'react'
import HLTV from 'hltv';
// Getting player id using this fn.
const getPlayerIdByName = async (text) => {
return await HLTV.getPlayerByName({ name: text })
.then(res => res.id)
// .then(data => console.log(data))
.catch(err => console.log(err));
}
//Getting player stats using id obtained from above
const getPlayerStats = (playerId) => {
HLTV.getPlayerStats({ id: playerId })
.then(res => Object.entries(res))
}
const Search = () => {
const [name, setName] = useState('');
const [id, setId] = useState('');
useEffect(() => {
getPlayerIdByName(name)
.then(id => setId(id))
}, [name]);
const onChange = (e) => {
setName(e.target.value)
}
const onSubmit = (e) => {
e.preventDefault();
setName(name);
console.log(name)
}
return (
<div>
<form onSubmit={onSubmit} className="player">
<input type="text" value={name} placeholder="Enter Player's in game name" onChange={onChange} />
<button type="Submit" defaultValue="Search">Search</button>
</form>
</div>
)
}
export default Search;

I would refactor your code like this:
The main problem I see, is that you are using useEffect() to get the playerIdByName every time that name changes. Instead, just call that function inside the onSubmit handler. And instead of storing the id in state, store your stats instead.
Then, when you have stats in state, you can render them by maping the key value pairs.
import HLTV from 'hltv';
// Getting player id using this fn.
const getPlayerByName = async (text) => await HLTV.getPlayerByName({ name: text });
//Getting player stats using id obtained from above
const getPlayerStats = async (playerId) => await HLTV.getPlayerStats({ id: playerId });
const Search = () => {
const [name, setName] = useState('');
const [stats, setStats] = useState([]);
const onChange = (e) => {
setName(e.target.value);
};
const fetchStats = async () => {
const player = await getPlayerByName(name);
const stats = await getPlayerStats(player.id);
const statsEntries = Object.entries(stats);
setStats(statsEntries);
};
const onSubmit = async (e) => {
e.preventDefault();
try {
await fetchStats();
} catch (error) {
console.error(error);
}
};
return (
<div>
<form onSubmit={onSubmit} className="player">
<input
type="text"
value={name}
placeholder="Enter Player's in game name"
onChange={onChange}
/>
<button type="Submit" defaultValue="Search">
Search
</button>
</form>
{stats.length > 0 && (
<div>
{stats.map(([key, value]) => (
<p>
{key}: {value}
</p>
))}
</div>
)}
</div>
);
};
export default Search;

Related

How to Search by name using the Rest country Api

When i search for a country, The searchInput useState works fine and receives the value, problem is it doesn't update the country in the Dom immediately unless i remove the useEffect hook dependency array, And this causes too many re-renders, So how can i update the DOM when i search search, Here is my code.
const countryUrl = `https://restcountries.com/v2/name`;
const [searchInput, setSearchInput] = useState<string>("Nigeria");
const [countryData, setCountryData] = useState<object>([]);
const fetchCountry = (searchInput: any) => {
axios
.get(`${countryUrl}/${searchInput}?fullText=true`)
.then((res) => setCountryData(res.data[0]))
.catch((err) => console.log(err));
};
useEffect(() => {
fetchCountry(searchInput);
}, []);
const handleSubmit = (e: any) => {
e.preventDefault();
fetchWeather(searchInput);
fetchCountry();
};
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setSearchInput(e.target.value)}
placeholder="Enter The Country"
type="text"
/>
<button type="submit">{<CiSearch />} </button>
</form>
Remove searchInput from fetchCountry = (searchInput: any) => {, it will look like fetchCountry = () => {
Reason is you have searchInput in useState so no need to pass as foo param
your fetchCountry will look like
const fetchCountry = () => { // removed params
axios
.get(`${countryUrl}/${searchInput}?fullText=true`)
.then((res) => setCountryData(res.data[0]))
.catch((err) => console.log(err));
};

How to pass a value from an input to a submit button?

I'm currently working on a project to implement a website to check the weather forecast.
I'm trying to get the value from the input field and when I click the submit button, this value should be set to cityName. What do I have to change in order to make this work?
import { useState, useEffect } from "react"
export function WeatherInfo() {
const token: string = '7ebe7c2a03cd48c090a193437'
async function getCurrentWeather(cityName: string): Promise<any> {
const response = await fetch(`http://api.weatherapi.com/v1/current.json?key=${token}&q=${cityName}`)
const data = await response.json()
console.log(data)
return data
}
const [cityName, setCityName]: any = useState('')
const [cityWeather, setCityWeather] = useState({})
const [value, setValue] = useState('')
const handleChange = (event: any) => {
setValue(event.target.value)
}
const handleSubmit = (event: any) => {
event.preventDefault()
setCityName(value)
}
useEffect(() => {
async function fetchData() {
const cityWeather = await getCurrentWeather(cityName)
}
fetchData()
})
return (
<div >
<form onSubmit={handleSubmit}>
<input onChange={handleChange} placeholder="Type here" />
<button>Search</button>
</form>
</div>
);
}
You should add a dependency array to your effect hook so that it triggers whenever cityName changes.
Updating the cityWeather state should only be done via the setCityWeather function.
useEffect(() => {
if (cityName) { // only fetch when you've got a value
getCurrentWeather(cityName).then(setCityWeather);
}
}, [cityName]);
You should also try to use as few any types as possible, preferably none
// define stand-alone functions outside your components
// eg weather-api.ts
const token = "your-api-key";
export interface CurrentWeather {
temp_c: number;
feelslike_c: number;
// etc
}
export async function getCurrentWeather(
cityName: string
): Promise<CurrentWeather> {
// safely encode URL query params
const params = new URLSearchParams({
key: token,
q: cityName,
});
const response = await fetch(
`http://api.weatherapi.com/v1/current.json?${params}`
);
// don't forget to check for errors
if (!response.ok) {
throw response;
}
return response.json(); // will be cast to the `CurrentWeather` type
}
import { useState, useEffect, FormEventHandler } from "react";
import { getCurrentWeather, CurrentWeather } from "./weather-api";
export function WeatherInfo() {
const [cityName, setCityName] = useState("");
const [cityWeather, setCityWeather] = useState<CurrentWeather>(); // default undefined
const [value, setValue] = useState("");
useEffect(() => {
getCurrentWeather(cityName).then(setCityWeather).catch(console.error);
}, [cityName]);
const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault();
setCityName(value);
};
return (
<div>
{cityWeather && (
<p>
The current temperature in {cityName} is {cityWeather.temp_c} °C
</p>
)}
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
placeholder="Type here"
/>
<button>Search</button>
</form>
</div>
);
}

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;

How to make my react component rerender when I submit a form into firestore

Please I am trying to insert data into my firestore and also want the data that I stored to immediately appear on my screen after submitting. The problem I am currently having is that once I insert the data into firestore, I have to reload the component before seeing it on my screen. Here is my code below, what am I Doing wrongly.
function CreateGroup({ currentUser }) {
const [name, setName] = useState("");
const [group, setGroup] = useState([]);
const handleChange = (e) => {
const { value, name } = e.target;
setName({
[name]: value,
});
};
const handleSubmit = async (e) => {
e.preventDefault();
createGroup(currentUser, name.name);
};
let id = currentUser ? currentUser.id : "";
useEffect(() => {
const fetchData = () => {
if (id) {
firestore
.collection("users")
.doc(id)
.collection("group")
.get()
.then(function (snapshot) {
snapshot.forEach(function (doc) {
// console.log(doc.id, " => ", doc.data());
setGroup({
id: doc.id,
...doc.data(),
});
});
});
}
};
fetchData();
}, [id]);
return (
<div>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="exampleInputTitle">Group Name</label>
<input
type="text"
className="form-control"
name="name"
id="name"
aria-describedby="TitleHelp"
onChange={handleChange}
/>
</div>
<button type="submit" className="btn btn-primary">
Add group{" "}
</button>
</form>
<div>
<div key={group.id}>
{group.name} {group.admin}
</div>
</div>
</div>
);
}
I would say, create a local state, say
const [submited, setSubmited] = useState(true);
then on submit, change the state to true, and in your useEffect use "submitted" state also as a dependency along with id. OnHandleChange set "submitted" to false so we can submit again.
if (id) {}
becomes
if (id && submitted) {}
in the UseEffect.
function CreateGroup({ currentUser }) {
const [name, setName] = useState("");
const [group, setGroup] = useState([]);
const [submited, setSubmited] = useState(true);
const handleChange = (e) => {
const { value, name } = e.target;
setName({
[name]: value,
});
setSubmited(false);
};
const handleSubmit = async (e) => {
e.preventDefault();
createGroup(currentUser, name.name);
setSubmited(true);
};
let id = currentUser ? currentUser.id : "";
useEffect(() => {
const fetchData = () => {
if (id && submitted) {
firestore
.collection("users")
.doc(id)
.collection("group")
.get()
.then(function (snapshot) {
snapshot.forEach(function (doc) {
// console.log(doc.id, " => ", doc.data());
setGroup({
id: doc.id,
...doc.data(),
});
});
});
}
};
fetchData();
}, [id, submitted]);
return (
<div>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="exampleInputTitle">Group Name</label>
<input
type="text"
className="form-control"
name="name"
id="name"
aria-describedby="TitleHelp"
onChange={handleChange}
/>
</div>
<button type="submit" className="btn btn-primary">
Add group{" "}
</button>
</form>
<div>
<div key={group.id}>
{group.name} {group.admin}
</div>
</div>
</div>
);
}
Yeah you can achieve this by checking first is firestore receive only:
-getting the data along after submission
-success response
the first scenario you will use .then to take the submitted data and set it to your local state.
the second scenario is to add another flag to your useeffect whenever you receive a successful response from firestore
const handleSubmit = async (e) => {
e.preventDefault();
/*
*if this method is async you can use .then
*/
await createGroup(currentUser, name.name)
.then(res=>
//here you can set a state to trigger useeffect
)
};
useEffect(() => {
const fetchData = () => {
if (id) {
firestore
.collection("users")
.doc(id)
.collection("group")
.get()
.then(function (snapshot) {
snapshot.forEach(function (doc) {
// console.log(doc.id, " => ", doc.data());
setGroup({
id: doc.id,
...doc.data(),
});
});
});
}
};
//no need for this
fetchData();
}, [id,yourstateaftersubmit]);

Api marvel response don't show react

I try to training in react and want to make a form who call the api marvel when submitted with the current input and display the name + description of the character search.
The Api call is ok but when i submit the form nothing show any advice?
import React, { Component, useEffect, useState } from 'react'
import axios from 'axios'
const SearchEngine = React.forwardRef((props, ref) => {
const [asked, setAsked] = useState([]);
const [characterInfos, setCharacterInfos] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [loading, setLoading] = useState(true);
const [inputs, setInputs] = useState('');
const handleChange = (event) => {
setInputs(event.target.value);
console.log(inputs);
}
const getCharacters = (inputs) => {
setSearchTerm(inputs)
axios
.get(`https://gateway.marvel.com:443/v1/public/characters?name=${searchTerm}&apikey=XXX`)
.then(response => {
console.log(searchTerm)
console.log(response)
setCharacterInfos(response.data.data.results[0]);
setLoading(false);
console.log(response.data.data.results[0].name)
response.data.data.results.map((item) => {
return characterInfos.push(item.name)
})
localStorage.setItem(characterInfos, JSON.stringify(response.data))
if (!localStorage.getItem('marvelStorageDate')) {
localStorage.setItem('marvelStorageDate', Date.now());
}
})
.catch(error => {
console.log(error);
})
}
return (
<div className="search-container">
<h1>Character Infos</h1>
<form onSubmit={getCharacters}>
<input
type="text"
placeholder="Search"
value={inputs}
onChange={handleChange}
/>
<input type="submit" value="Envoyer" />
</form>
<ul>
<li>{characterInfos.name}</li>
</ul>
</div>
)
})
export default React.memo(SearchEngine)
Thanks for your help. Any to advice to show a list of all the character and make a search filter who work with minimum 3 characters?
getCharacters is fired with form submit event as param. You are assuming that is getting inputs from the state wrongly:
const getCharacters = event => {
event.preventDefault() // Prevent browser making undesired form native requests
// setSearchTerm(inputs); // Not sure what are you trying here but, again, inputs is a form submit event
axios
.get( // use searchValue as query string in the url
`https://gateway.marvel.com:443/v1/public/characters?name=${searchValue}&apikey=XXX`
)
.then(response => {
console.log(searchTerm);
console.log(response);
setCharacterInfos(response.data.data.results[0]);
setLoading(false);
console.log(response.data.data.results[0].name);
response.data.data.results.map(item => {
return characterInfos.push(item.name);
});
localStorage.setItem(characterInfos, JSON.stringify(response.data));
if (!localStorage.getItem("marvelStorageDate")) {
localStorage.setItem("marvelStorageDate", Date.now());
}
})
.catch(error => {
console.log(error);
});
};

Resources