Fetching data with input parameter with SWR Hook - reactjs

function DayOne() {
const [country, setCountry] = useState("");
const url = `${process.env.REACT_APP_BASE_URL}/dayone/all/total/country/${country}`;
const { data, error } = useSWR(url, fetcher);
let value = useRef("");
const onClick = async (e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
return setCountry(value.current);
};
const onChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
value.current = e.target.value;
};
let index = 1;
if (error) return <div>failed to load</div>;
if (!data) return <Loading />;
const { name } = data;
return (
<div>
{console.log(data)}
<ContainerComp>
<NavBar />
{console.log(country)}
<InputCountryForm myRef={value} onChange={onChange} onClick={onClick} />
<div>{country}</div>
{name &&
name.map((n: IDayOne) => {
<CustomCard icon={""} title={country} value={n.Active} />;
})}
</ContainerComp>
</div>
);
}
export default DayOne;
the fetcher
export const fetcher = (url: string) => fetch(url).then((res) => res.json());
Im trying to display a card list with values coming after picking a country a submitting to the endpoint. How can i improve this and actually make it work ?

Related

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>
);
}

React Context loosing state on page refresh

So basicaly i have a subreddit context where i get a bunch of subreddis from the api
import axios from "axios";
import { createContext, useCallback, useEffect, useState } from "react";
import { getUniqueObjects } from "../Helpers/Helpers";
import { ChildrenType } from "../Types/ProviderChildrenType";
import { Subreddit, SubredditsResponse } from "../Types/Subreddits";
type InitialState = {
subredditsData?: Subreddit[];
subredditsLoading?: boolean;
subredditsError?: boolean;
subredditsHasMore?: boolean;
getSubreddits?: (arg0: string) => void;
};
export const SubredditContext = createContext<InitialState>({});
export const SubredditContextProvider = ({ children }: ChildrenType) => {
const [data, setData] = useState<Subreddit[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [hasMore, setHasMore] = useState(true);
const UseSubreddits = (url: string) => {
const apiCall = useCallback(() => {
setLoading(true);
axios
.get(url)
.then((response: SubredditsResponse) => {
setData(getUniqueObjects([...data, ...response.data]));
setHasMore(response.data.length > 0);
setLoading(false);
})
.catch((err) => {
setError(err);
});
}, [url]);
useEffect(() => apiCall(), [apiCall]);
};
return (
<SubredditContext.Provider
value={{
subredditsData: data,
subredditsLoading: loading,
subredditsError: error,
subredditsHasMore: hasMore,
getSubreddits: UseSubreddits,
}}
>
{children}
</SubredditContext.Provider>
);
};
In my home page I trigger the custom hook of the context "UseSubreddits" which I pass it as the "getSubreddits" prop,
function Homepage() {
const navigate = useNavigate();
const [pageNumber, setPageNumber] = useState(1);
const {
subredditsData,
subredditsLoading,
subredditsError,
subredditsHasMore,
getSubreddits,
} = useContext(SubredditContext);
getSubreddits!(`https://6040c786f34cf600173c8cb7.mockapi.io/subreddits?page=${pageNumber}&limit=16`)
window.addEventListener("scroll", () => {
if (
window.scrollY + window.innerHeight >=
document.documentElement.scrollHeight &&
subredditsHasMore
) {
setPageNumber(pageNumber + 1);
}
});
return (
<>
<Navbar pageTitle="subreddits" />
<div className="homepage">
<div className="homepage__subreddits">
{subredditsData?.map((item) => {
return (
<div key={item.id}>
<SubredditCard
key={item.id}
onClick={() => navigate(`/posts/${item.id}`)}
title={item.title}
description={item.description}
/>
</div>
);
})}
</div>
</div>
<div className="homepage__text">
{subredditsLoading && <h2>Loading...</h2>}
{subredditsError && (
<h2>An error has occured please refresh your page.</h2>
)}
</div>
</>
);
}
export default Homepage;
I have the same kind of context file where I get the posts of the selected subreddit
type InitialState = {
postData?: Post[];
postLoading?: boolean;
postError?: boolean;
getPost?: (arg0: string) => void;
voteHandler?: (
arg0: string,
arg1: string,
arg2: boolean,
arg3: boolean
) => void;
};
export const PostContext = createContext<InitialState>({});
export const PostContextProvider = ({ children }: ChildrenType) => {
const [data, setData] = useState<Post[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const UsePosts = (url: string) => {
const apiCall = useCallback(() => {
setLoading(true);
axios
.get(url)
.then((response) => {
setData(response.data);
setLoading(false);
})
.catch((err) => {
setError(err);
});
}, [url]);
useEffect(() => {
apiCall();
}, [apiCall]);
};
return (
<PostContext.Provider
value={{
postData: data,
postLoading: loading,
postError: error,
getPost: UsePosts,
}}
>
{children}
</PostContext.Provider>
);
};
and then I do the same thing in the Post component as I do in the Homepage
function Posts() {
const { subredditId } = useParams();
const [urlParam, setUrlParam] = useState("");
const {
postData,
postLoading,
postError,
getPost,
voteHandler,
} = useContext(PostContext);
const { subredditsData } = useContext(SubredditContext);
const selectedSubreddit = useMemo(
() => subredditsData!.find((subreddit) => subreddit.id === subredditId),
[subredditsData]
);
const navigate = useNavigate();
const sortByTitle = "?sortBy=title";
getPost!(
`https://6040c786f34cf600173c8cb7.mockapi.io/subreddits/${subredditId}/posts${urlParam}`
);
return (
<>
<Navbar pageTitle={selectedSubreddit!.title} />
<div className="posts-screen">
<div className="posts-screen__left-panel">
<div className="posts-screen__left-panel-content">
<SortBy onClick={() => setUrlParam(sortByTitle)} />
<div className="posts-screen__posts-container">
{postData?.map((post) => {
return (
<PostCard
key={post.id}
id={post.id}
title={post.title}
description={post.body}
user={post.user}
voteCount={post.upvotes - post.downvotes} ...
Everything works fine except that in the post screen if I reload the page the subreddit state is lost and the posts screen gives and error. I know that the state is lost on refresh but shouldn't it make the api call again for the subreddits?
I'm new at Context so I don't know how to handle it better

Read data from form in custom react hook

How to get data from form in custom hook? I want to call useSearch hook in SearchProducts component and pass the submitted data. Is there any better solution for this?
function useSearch() {
const submitHandle = (event) => {
event.preventDefault();
let data = [];
const elements = event.target.childNodes;
elements.forEach((element) => {
data[element.name] = element.value;
element.value = "";
});
// I need data from form here.
console.log(data);
};
return {
onSubmit: submitHandle,
};
}
export default function SearchProducts() {
const search = useSearch();
return (
<Container>
<Form {...search}>
<Input name="searchInput" />
<SearchButton type="submit">search</SearchButton>
</Form>
</Container>
);
}
export default function Input({ type, name }) {
const input = useInput("");
return <InputField type={type} name={name} {...input} />;
}
function useInput(initialValue) {
const [value, setValue] = useState(initialValue);
const handleChange = (event) => {
if (value === false) {
setValue(true);
}
setValue(event.target.value);
};
return {
value: value,
onChange: handleChange,
};
}

React Hooks setState function is not a function error

I'm getting this error in React Hooks. The function exists but every time I type something in to the search bar I get this TypeError.
TypeError : setSearchField is not a function
Here's the code for reference :
export default function StudentAPI() {
const [searchField, setSearchField] = ('');
const [students, setStudents] = useState([]);
const getStudents = async () => {
return axios
.get("https://api.hatchways.io/assessment/students")
.then((res) => {
setStudents(res.data.students);
})
.catch((err) => console.log(err));
};
useEffect(() => {
getStudents();
}, []);
const handleChange = (e) => {
setSearchField(e.target.value);
}
const filteredStudents = students.filter((student) => {
console.log(student.firstName);
// return student.firstName.toLowerCase().includes(search.toLowerCase()) ||
// student.lastName.toLowerCase().includes(search.toLowerCase());
})
return (
<div className="container">
<SearchBox
placeholder={'Search by name'}
handleChange={handleChange}
value={searchField}
/>
{filteredStudents.length > 0 ? filteredStudents.map((student) => {
return <Student key={student.id} student={student}/>;
}) : students.map((student) => {
return <Student key={student.id} student={student}/>;
})}
</div>
);
};
You have to use the hook useState
const [searchField, setSearchField] = usestate('');
You must have the state declaration above
const [searchField,setSearchField]=useState()
You have an error because useState is not written!
You must change
const [searchField, setSearchField] = ('');
to
const [searchField, setSearchField] = useState('');

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;

Resources