React useEffect for Loading Data - reactjs

I'm learning React Hooks and I'm wondering what would be the most effective way to reload data while being "hook friendly".
I've identified 3 use cases (the latest apparently being the "more appropriate"
With Copied Code
//Example of Using useEffect Hooks by duplicating code
import React, {useState, useEffect} from 'react'
import axios from 'axios'
export default () => {
const [deals, setDeals] = useState([])
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
axios({
method: 'GET',
url: `http://localhost:1338/deals`
}).then(res => {
setDeals(res.data)
setLoading(false)
})
}, [setDeals])
return(
<div className="Deals">
{loading &&
<p>It's loading</p>
}
{!loading &&
<>
{deals.map((deal, i) => (
<div key={i} className="Deal Note">
{deal.label}
</div>
))}
</>
}
<button onClick={() => {
setLoading(true)
axios({
method: 'GET',
url: `http://localhost:1338/deals`
}).then(res => {
setDeals(res.data)
setLoading(false)
}).catch(res => {
setDeals([{label: 1, label: 2}])
setLoading(false)
})
}}>Fetch Again</button>
</div>
)
}
By Passing Hooks inside an external function. Code reuse - using hooks inside another function
I'm understanding this is not "the way" to use hooks although this was my first go-to solution
//Example of Using useEffect Hooks by feeding hooks to external function
import React, {useState, useEffect} from 'react'
import axios from 'axios'
const usefetchMore = (setDeals, setLoading) => {
console.log("usefetchMore")
setLoading(true)
axios({
method: 'GET',
url: `http://localhost:1338/deals`
}).then(res => {
setDeals(res.data)
setLoading(false)
})
}
export default () => {
const [deals, setDeals] = useState([])
const [loading, setLoading] = useState(false)
useEffect(() => {
usefetchMore(setDeals, setLoading)
}, [setDeals])
return(
<div className="Deals">
{loading &&
<p>It's loading</p>
}
{!loading &&
<>
{deals.map((deal, i) => (
<div key={i} className="Deal Note">
{deal.label}
</div>
))}
</>
}
<button onClick={() => usefetchMore(setDeals, setLoading)}>Fetch Again</button>
</div>
)
}
This one seem to be "the proper way of doing it" and is based on having useEffect re-triggered because it's listening to the changes on the reload variable which is there just to re-trigger it.
//Example of Using useEffect Hooks with variable to re-trigger useEffect
import React, {useState, useEffect} from 'react'
import axios from 'axios'
/* DOESN't WORK */
export default () => {
const [deals, setDeals] = useState([])
const [loading, setLoading] = useState(false)
const [reload, setReload] = useState(0)
useEffect(() => {
console.log("Deal4.useEffect")
setLoading(true)
axios({
method: 'GET',
url: `http://localhost:1338/deals`
}).then(res => {
setDeals(res.data)
setLoading(false)
})
}, [setDeals, reload])
return(
<div className="Deals">
{loading &&
<p>It's loading</p>
}
{!loading &&
<>
{deals.map((deal, i) => (
<div key={i} className="Deal Note">
{deal.label}
</div>
))}
</>
}
<button onClick={() => {
setReload(reload + 1)
}}>Fetch Again</button>
</div>
)
}
My question is: If I were to build a component that shows loading and allows to refresh itself, which way would be the proper way to write it with "React hooks"?

Create a component in /src/ called Photos.js and give it a basic list:
import React from "react";
import { useFetch } from "./hooks";
function Photos() {
const [data, loading] = useFetch(
"https://jsonplaceholder.typicode.com/photos?albumId=1"
);
return (
<>
<h1>Photos</h1>
{loading ? (
"Loading..."
) : (
<ul>
{data.map(({ id, title, url }) => (
<li key={`photo-${id}`}>
<img alt={title} src={url} />
</li>
))}
</ul>
)}
</>
);
}
export default Photos;
Now we need a Hook! Create a file in the same directory called hooks.js and fill it with this:
import { useState, useEffect } from "react";
function useFetch(url) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
async function fetchUrl() {
const response = await fetch(url);
const json = await response.json();
setData(json);
setLoading(false);
}
useEffect(() => {
fetchUrl();
}, []);
return [data, loading];
}
export { useFetch };
Import the Photos component into App.js and yarn start. Done!

Related

Unexpected token < in JSON at position 0 while trying to make link dynamic

I have checked in case there is an error or typo. I don't know if it is my res.json() file that is causing it.
I used usefetch to also import data from the API. so I had to pass it as JSON.
now I want to make each blog link dynamic.
my usefetch.js file
import { useState, useEffect} from 'react';
const useFetch = (url) =>{
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect( ()=>{
fetch(url)
.then(res =>{
if (!res.ok){
throw Error('could not fetch data');
}
return res.json();
})
.then(data=>{
setData(data.blogs);
setError(null);
})
.catch(err =>{
setError(err.message);
})
},
[url]);
return { data, error }
}
export default useFetch;
this is where I am getting the error. the link is correct.
BlogDetails.js file
import { useParams } from "react-router-dom";
import useFetch from "./useFetch";
const BlogDetails = () => {
const { id } = useParams()
const { data: blog, error} = useFetch('http://localhost:3000/blog/' + id);
return (
<div className="blog-details">
{error && <div>{error}</div>}
{ blog && (
<article>
<h2> {blog.title} </h2>
<p>by {blog.author}</p>
<div> {blog.body} </div>
</article>
)}
</div>
where i fetched my data from api
home.js file
//import { useState, useEffect } from 'react';
import Bloglist from './BlogList';
import useFetch from './useFetch';
const Home = () =>{
const { data: blogs, error } = useFetch("http://localhost/blogphpapi/views/getapi.views.php");
return(
<div className="home">
{error && <div>{error}</div>}
<h1>Home page content</h1>
{// {isPending && <div>Loading ...</div>}
} {blogs && <Bloglist blogs={blogs} title='Recent Updates' />}
</div>
);
}
export default Home;

rendering sorted array of objects using UseMemo

I'm trying to render the sorted array of objects using useMemo. Currently the last sorted array is rendering on the screen. But i want to use the select drop down where users can select different sort like title shown in code using useMemo. The users can sort by selecting title, author image.
I have used redux for sorting the array of objects.Could someone please help me with the best practice. Thanks.
I have added Post.js below the HomePage.js. Is my approach to it is wrong? Should i change the approach?
Any suggestions will be helpful.Could someone suggest me the best practies for it. Any suggestions on what am i doing wrong here?
HomePage.js
import React, { useState, useEffect, useMemo } from "react";
import Post from "../../Components/Post/Post";
import "./HomePage.css";
import axios from "axios";
const HomePage = () => {
const [posts, setPosts] = useState("");
let config = { Authorization: "................" };
const url = ".........................";
useEffect(() => {
AllPosts();
}, []);
const AllPosts = () => {
axios
.get(`${url}`, { headers: config })
.then((response) => {
const allPosts = response.data.articles;
console.log(response);
})
.catch((error) => console.error(`Error: ${error}`));
};
const newPostsByTitle = useMemo(() => {
allPosts.sort((a, b) => a.title.localeCompare(b.title)), [posts];
});
return (
<div className="home">
<div className="select">
<select
name="slct"
id="slct"
onChange={(e) => newPostsByTitle(e.target.value)}
></select>
</div>
<Post className="Posts" posts={posts} key={posts.title} />
</div>
);
};
export default HomePage;
Post.js
import React from "react";
import "./Post.css";
import { Fragment } from "react";
const Post = (props) => {
const displayPosts = (props) => {
const { posts } = props;
if (posts.length > 0) {
return posts.map((post) => {
return (
<Fragment>
<div className="Post" key={post.title}>
<img
src={post.urlToImage}
alt="covid"
width="100%"
className="img"
/>
<h5 className="title"> {post.title}</h5>
<p className="author"> {post.author}</p>
<p className="description"> {post.description}</p>
</div>
</Fragment>
);
});
}
};
return <div className="Posts">{displayPosts(props)}</div>;
};
export default Post;
There are few issues with the useMemo function.
There is no allPosts variable that will be available for that
function
There is no return inside useMemo
The dependency array syntax is wrong.
It should be something like the following.
const newPostsByTitle = useMemo(() => {
return [...posts].sort((a, b) => a.title.localeCompare(b.title));
}, [posts]);

Problem with array - Cannot read property 'map' of undefined

I'm trying to do a Netflix clone using React but I've gotten this error and I'm not sure where the error is.
import React, { useState, useEffect } from 'react';
import axios from "./axios"
import "./Row.css"
const base_url = "https://image.tmdb.org/t/p/original/"
function Row({ title, fetchUrl, isLargeRow }) {
const [movies, setMovies] = useState([]);
useEffect(() => {
async function fetchData() {
const request = await axios.get(fetchUrl);
setMovies(request.data.results);
return request;
}
fetchData();
}, [fetchUrl]);
console.table(movies);
return (
<div className="row">
<h2>{title}</h2>
<div className="row__posters">
{movies.map((movie) => (
<img
key={movie.id}
className={`row__poster ${isLargeRow && "row__posterLarge"}`}
src={`${base_url}${movie.poster_path}`}
alt={movie.name}
/>
))}
</div>
</div>
);
}
export default Row
The error I keep getting is

How can I send a request to API with on click of my button in React hooks?

So I currently have this code that has a useEffect() that shows the data from the API every time. I refresh, but I'm trying to make that data display only when I click on my button. I'm not too sure where to go with my code.
import React, { useState, useEffect } from 'react';
import './App.css';
import axios from 'axios';
function App() {
const [image, setImage] = useState(false);
// I tried to put a onclick function, but not sure what to add here
const handleChange = ()
=> {
setImage(true)
}
// this code displays my data on refresh
useEffect(() => {
axios
.get(
'https://api.com'
)
.then(res => {
setImage ?
setImage(res.data.faces[0].urls[4][512]) : console.log('nothing')
})
.catch(err => {
console.log(err.message);
});
}, []);
return (
<div className='App'>
<h1>Photo Generator</h1>
<img src={image} />
<button onClick={handleChange}>Show new Image</button>
</div>
);
}
I've updated your code.
Try this code, let me know if it works for you. :)
import React, { useState } from "react";
import "./App.css";
import axios from "axios";
function App() {
const [image, setImage] = useState(false);
// I tried to put a onclick function, but not sure what to add here
const handleChange = () => {
axios
.get("https://api.com")
.then(res => {
const uri = res.data.faces[0].urls[4][512];
if (uri) {
setImage(uri);
} else {
console.log("nothing");
}
})
.catch(err => {
console.log(err.message);
});
};
return (
<div className="App">
<h1>Photo Generator</h1>
{image && <img src={image} alt="yourImage" />}
<button type="button" onClick={handleChange}>
Show new Image
</button>
</div>
);
}
You don't need to use useEffect Hook in this case.
also don't need to check setImage inside of API callback function.
You could do it like this
import React, {useState, useEffect} from 'react';
import './App.css';
import axios from 'axios';
function App() {
const [image, setImage] = useState('');
const [displayImage, setDisplayImage] = useState('none');
const handleChange = () => {
setDisplayImage('flex');
};
useEffect(() => {
axios
.get('https://api.com')
.then((res) => {
setImage
? setImage(res.data.faces[0].urls[4][512])
: console.log('nothing');
})
.catch((err) => {
console.log(err.message);
});
}, []);
return (
<div className="App">
<h1>Photo Generator</h1>
<div style={{display: displayImage}}>
<img style src={image} />
</div>
<button onClick={() => handleChange()}>Show new Image</button>
</div>
);
}
You could also do
<button onClick={() => setDisplayImage('flex')}>Show new Image</button>

useContext hook update from child component

I'm fairly new to hooks as well as useContext, I'm attempting to update onClick from a child component and keep running into the error 'setPokemonId' is not a function, or.. it just doesn't do anything at all.
PokedexContext.js
import React, { useState, useEffect } from "react";
import axios from "axios";
const PokedexContext = React.createContext([{}, () => {}]);
const PokedexProvider = (props) => {
const [state, setState] = useState({});
const [loading, setLoading] = useState(true);
let [error, setError] = useState(false);
let [pokemonId, setPokemonId] = useState(10); //807 max
useEffect(() => {
setLoading(true);
setError(false);
axios
.get(`https://pokeapi.co/api/v2/pokemon/${pokemonId}`)
.then((res) => {
setState(res.data, loading);
setLoading(false);
})
.catch((err) => {
setLoading(false);
if (err.response) {
// pokemon not found
setError("Response");
} else if (err.request) {
// api error
setError("Request");
} else {
// everything else
setError(true);
}
});
}, [pokemonId]);
return (
<PokedexContext.Provider
value={[
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId,
]}
>
{props.children}
</PokedexContext.Provider>
);
};
export { PokedexContext, PokedexProvider };
App.js
import React from "react";
import { PokedexProvider } from "../../context/PokedexContext";
import Pokedex from "../Pokedex";
const Landing = () => {
return (
<PokedexProvider>
<main className="container">
<Pokedex />
</main>
</PokedexProvider>
);
};
export default Landing;
Pokedex.js
import React, { useState, useContext } from "react";
import pokedex from "../assets/pokedex.png";
import PokedexScreen from "./PokedexScreen";
import errorHandling from "../utils/errorHandling";
import { PokedexContext } from "../context/PokedexContext";
import spinner from "../assets/pika-load.gif";
const Pokedex = () => {
const [state, setState, error, loading, setPokemonId, pokemonId] = useContext(
PokedexContext
);
const [power, setPower] = useState(false);
const [shinyDisplay, setShinyDisplay] = useState(false);
console.log(pokemonId);
return (
<div
alt="Pokedex"
data-testid="Pokedex"
className="pokedex"
style={{ backgroundImage: `url(${pokedex})` }}
>
<button className="pokedex--onButton" onClick={() => setPower(!power)}>
{power ? "Off" : "On"}
</button>
{error ? (
<div className="pokedex--screen__error">{errorHandling(error)}</div>
) : power ? (
<>
{loading ? (
<img
className="pokedex--screen__load"
src={spinner}
alt="Loading..."
/>
) : (
""
)}
<button
className="pokedex--shinyButton"
onClick={() => setShinyDisplay(!shinyDisplay)}
>
S
</button>
<span>
<button
className="pokedex--negativeButton"
onClick={() => setPokemonId(pokemonId - 1)}
>
-
</button>
<button
className="pokedex--positiveButton"
// onClick={}
>
+
</button>
</span>
</>
) : (
<div className="pokedex--screen__off" />
)}
</div>
);
};
export default Pokedex;
My goal here is to update the pokemonId when clicking the positive or negative buttons, and is what is currently causing the crashes.
Issue
When you're exporting your values in Context Provider. It's in the 6th index of the array
<PokedexContext.Provider
value={[
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId, (6)
]}
>
{props.children}
</PokedexContext.Provider>
But you're using from the 4th index in Pokedex.
const [state, setState, error, loading, setPokemonId, pokemonId] = useContext(
PokedexContext
);
Solution
const [
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId, (6)
] = useContext(
PokedexContext
);

Resources