useState dosen't work in react - useSelector, useState, useEffect - javascript

To load more images, the list should accept the values of images, but it's not working.
Why can't I get the value of the list? (list.length=0 in console)
const { images, imagesLoaded } = useSelector((state: RootState) => state.gallery);
const dispatch = useDispatch();
const [imageUrl, setImageUrl] = useState('');
useEffect(() => {
if(!imagesLoaded) {
dispatch(getImages());
}
// eslint-disable-next-line
}, []);
const [list, setList] = useState([...images.slice(0, 5)])
console.log(images.slice(0, 5))
console.log(list.length)
const [loadMore, setLoadMore] = useState(false)
const [hasMore, setHasMore] = useState(images.length > 5)
const handleLoadMore = () => {
setLoadMore(true)
}

Becasue images only has value after you call dispatch(getImages());
const [list, setList] = useState([...images.slice(0, 5)]) will declare the initial value for list and not update when images update.
if you want update list when images update, you can use useEffect:
useEffect(() => {
setList(images.slice(0, 5))
// eslint-disable-next-line
}, [images]);

Related

What would be the best solution to avoid this infinite loop (useEffect)

I developed an application where I get an api (pokeAPI) with pokemons, and basically I have a global array with "myPokemons", so I want to display all my pokemons except the ones in that array, so I created the function "filterMyPokemons" that I filter the pokemons that should be displayed, and then I call this function in useEffect so that it is updated along with the page, putting a dependency array from the API list. The problem is that I now have an infinite loop that hurts the performance of the application.
import * as C from './styles';
import logo from '../../assets/pokelogo.png';
import { useContext, useState } from 'react';
import { useApi } from '../../hooks/useApi';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import Pokelist from '../../components/PokeList';
import CatchingPokemonIcon from '#mui/icons-material/CatchingPokemon';
import CatchContext from '../../context/Context';
const Homepage = () => {
const api = useApi();
const { showMyPokemons } = useContext(CatchContext);
const navigate = useNavigate();
const [pokemonList, setPokemonList] = useState([]);
const [loading, setLoading] = useState(false);
const [text, setText] = useState('');
const [myPokemonsList, setMyPokemonsList] = useState([]);
const [pokemonListFiltered, setPokemonListFiltered] = useState([]);
useEffect (() => {
const getAllPokemons = async () => {
const myPokemons = await showMyPokemons();
const pokemon = await api.getAllPokemon();
setLoading(true);
setPokemonList(pokemon);
setMyPokemonsList(myPokemons);
setLoading(false);
}
filterMyPokemons();
getAllPokemons();
}, [myPokemonsList]);
const filterMyPokemons = async () => {
const filteredList = await pokemonList.filter(pokemons => !myPokemonsList.includes(pokemons.name))
return setPokemonListFiltered(filteredList);
};
const lowerSearch = text.toLocaleLowerCase();
const filteredPokemons = pokemonListFiltered.filter(pokemon => pokemon
.name.toLowerCase().includes(lowerSearch)
);
const handleHome = () => {
navigate('/')
}
const handleMyPokemons = () => {
navigate('/mypokemons')
}
return (
<C.Container>
<C.Logo>
<img src={logo} alt="" />
</C.Logo>
<C.Navbar>
<input
type="text"
placeholder='Busque um pokémon...'
onChange={(e) => setText(e.target.value)}
value={text}
/>
</C.Navbar>
<C.Pokedatabase onClick={handleMyPokemons}>
<button>Meus pokémons <i><CatchingPokemonIcon /></i></button>
</C.Pokedatabase>
<C.Pokelist>
{filteredPokemons.map(pokemon => {
return (
<Pokelist
name={pokemon.name}
/>
)
})}
</C.Pokelist>
</C.Container>
)
}
export default Homepage;
If I leave useEffect's dependency array empty, the items are not displayed, but if I leave any dependencies it causes an infinite loop. How to solve this problem?
The problem comes with updating the myPokemonsList array within the useEffect hook that depends on that array.
useEffect (() => {
const getAllPokemons = async () => {
const myPokemons = await showMyPokemons();
const pokemon = await api.getAllPokemon();
setLoading(true);
setPokemonList(pokemon);
setMyPokemonsList(myPokemons); // Here's the infinite loop
setLoading(false);
}
filterMyPokemons();
getAllPokemons();
}, [myPokemonsList]); // Here's the infinite loop
You should have another use effect for updates on the myPokemonList in order to avoid updating and depending on the same list.

React Add filtering on pagination

I have a product page sorted by title. Sorting works, everything is fine, but when pagination occurs, new items are loaded without sorting. How to fix it?
const Grid = ({filter, countOnRow, totalCount, products}) => {
const [fetching, setFetching] = useState(true)
const [total, setTotal] = useState(totalCount)
const [startPage, setStartPage] = useState(0)
const [produs, setProdus] = useState([])
const [okay, setOkay] = useState(false)
const [selectedSort, setSelectedSort] = useState('')
useEffect(()=>{
if(fetching){
setProdus([...produs, ...products.slice(startPage,total)])
setStartPage(total)
setTotal(total+total)
setFetching(false )
}
}, [fetching])
useEffect(()=>{
document.addEventListener('scroll', scrollHadnler);
return function(){
document.removeEventListener('scroll', scrollHadnler)
};
}, [])
const scrollHadnler=(e)=>{
if(e.target.documentElement.scrollHeight-(e.target.documentElement.scrollTop+window.innerHeight)<100){
setFetching(true)
}
}
const sortByName = (sort) => {
setSelectedSort(sort)
setProdus([...produs].sort((a,b) => a[sort].localeCompare(b[sort])))
setOkay(!okay)
}
How can I implement the filtering to persist after pagination.

How can I fetch and pass the result to the next fetch in react?

How can I fetch the version and languages and pass them to getChampions function
const [version, setVersion] = useState(0)
const [languages, setLanguages] = useState([])
const [selectedLanguage, setSelectedLanguage] = useState('')
const [champions, setChampions] = useState([])
useEffect(() => {
getVersion().then((version) => setVersion(version))
.then(getLanguages().then(languages => {
setLanguages(languages)
setSelectedLanguage(languages[0])
}))
.then(getChampions(version, selectedLanguage).then(champions => setChampions(champions)))
}, [])
I'm getting the default values from the initialization of useState where version = 0 and languages = []
setState is asynchronous, so if you setState and then call a function with the state immediately after you are not guaranteed to get the current state value. #Yadab's answer resolves this but calling getChampions with the variables from the response rather than the variables from the state.
My personal preference is to use a separate hook to respond to changes in the state. It also seems like getVersion and getLanguages don't depend on each other and can be run simultaneously rather than one after the other.
const App = () => {
const [version, setVersion] = useState(0);
const [languages, setLanguages] = useState([]);
const [selectedLanguage, setSelectedLanguage] = useState("");
const [champions, setChampions] = useState([]);
useEffect(() => {
getVersion().then(setVersion);
}, []); // run once - include [setVersion] if using eslint
useEffect(() => {
getLanguages().then((languages) => {
setLanguages(languages);
setSelectedLanguage(languages[0]);
});
}, []); // run once - can include deps [setLanguage, setSelectedLanguage] for eslint
useEffect(() => {
// only execute if both version and selectedLanguage have already been set
if (version && selectedLanguage) {
getChampions(version, selectedLanguage).then(setChampions);
}
}, [version, selectedLanguage]); // run whenever version or selected language changes
...
You can use async await in a separate function to fetch the version and language and use the fetched version and language to fetch the champions data. Take a look at below example.
const [version, setVersion] = useState(0)
const [languages, setLanguages] = useState([])
const [selectedLanguage, setSelectedLanguage] = useState('')
const [champions, setChampions] = useState([])
const fetchData = async () => {
const versionData = await getVersion();
setVersion(versionData)
const languageData = await getLanguages();
setLanguages(languageData)
setSelectedLanguage(languageData[0])
const championsData = await getChampions(versionData, languageData[0])
setChampions(championsData)
}
useEffect(() => {
fetchData();
}, [])

How use Local Storage in Functional Component React

How can I use LocalStorage in a functional component like this
I know how do this in a class component but can I solve this problem in this case?
ERROR: TypeError: repositories is not a function
export default function Main() {
const [newRepo, setNewRepo] = useState('');
const [repositories, setRepositories] = useState([]);
const [clearInput] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
repositories(localStorage.getItem('repositories'));
if (repositories) {
setRepositories(JSON.parse(repositories));
}
}, [repositories]);
useEffect((_, prevState) => {
if (prevState.repositories !== repositories) {
localStorage.setItem('repositories', JSON.stringify(repositories));
}
});
In your first useEffect, the repositories is your state which an array. Not a function.
Also, in your second useEffect you need to make correction to the way you access the prevState in hooks.
Fix for 1st useEffect
export default function Main() {
const [newRepo, setNewRepo] = useState('');
const [repositories, setRepositories] = useState([]);
const [clearInput] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const localRepoItems = localStorage.getItem('repositories');
if (localRepoItems) {
setRepositories(JSON.parse(localRepoItems));
}
}, []); // do not give the dependency as repositories as it will go to infinite loop
});
To obtain previous state in hooks, you can write a little custom hook:
Like this:
export const usePrevious = value => {
const ref = React.useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
Usage in your component:
const prevRepositories = usePrevious(repositories);
useEffect(() => {
if (prevRepositories.length !== repositories.length) {
localStorage.setItem('repositories', JSON.stringify(repositories));
}
}, [repositories]);

React Hook's state not getting updated

I've built a React Hook as follows:
const Index = (props) => {
const [posts, setPosts] = useState([])
useEffect(() => {
const getPosts = async () => {
const posts = await getPostFromWebService()
for (let i of posts) {
setPosts([ ...posts, i ])
}
}
getPosts()
}, [])
// ... remaining code
}
But even if the web service returns 5 posts, only the last posts is getting updated in the posts state. Hence it only receives one post in it, instead of 5.
What am I doing wrong here?
It sounds like you want something like this. Here we would have the useEffect listen for any changes in postCount so that we can trigger your logic to fetch more posts.
const Index = (props) => {
const [posts, setPosts] = useState([])
const [postCount, setPostCount] = useState(0)
useEffect(() => {
const getPosts = async () => {
const newPosts= await getPostFromWebService()
setPosts([...posts, newPosts])
}
}, [postCount])
return(
<div>
<button onClick={() => setPostCount(postCount + 5)}>Get more posts</button>
</div>
)
}

Categories

Resources