I want to make an infinite scrolling. The idea is next, when user scroll at the bottom of the scroll area, the http request should occur and to add data to the previous, that exists before. In this way the user if will scroll back to the top will be able to see all options.
For this i created:
import React, { useState } from "react";
import AsyncSelect from "react-select/async";
const WithPromises = () => {
const [page, setPage] = useState(1);
const [allData, setAllData] = useState([]); //here should be added all data
const filterData = (inputValue) => {
const req = fetch(
`https://jsonplaceholder.typicode.com/todos?_limit=15&_page=${page}`
)
.then((response) => response.json())
.then((res) => {
console.log(res, "data");
return res.map(({ title }) => {
return {
label: title,
value: title
};
});
});
return req;
};
const promiseOptions = (inputValue) => {
return filterData(inputValue);
};
const scroll = (e) => {
setPage(page + 1); //when scroll is at the bottom
};
console.log(page);
return (
<AsyncSelect
cacheOptions
onMenuScrollToBottom={scroll}
isClearable={true}
isSearchable={true}
defaultOptions
loadOptions={promiseOptions}
/>
);
};
export default WithPromises;
How you can see i increment the page every time the user is at the bottom of the scroll area.
setPage(page + 1). The value is added in the request.
Question: How to achieve what i decsribed above? When i will scroll down the new values should be added in the whole list and to be saved there and if i will scroll again at the boottm the the page should change and again new data should be added in the whole list.
demo: https://codesandbox.io/s/codesandboxer-example-forked-zsj6i?file=/example.js:0-1070
react-virtualized have an InfiniteLoader HOC which you can use for the implementation of your infinite scrolling menu, let me give you an pseudocode:
function App() {
const [items, setItems] = React.useState([]);
const [rowCount, setRowCount] = React.useState(0);
const rowRenderer = ({ key, index, style }) => (
<div key={key} style={style}>
{items[index]}
</div>
);
const isRowLoaded = ({ index }) => {
return !!items[index];
};
const loadMore = ({ startIndex, stopIndex }) => {
fetch(`https://blahblahblah.com/getData?from=${startIndex}&to=${stopIndex}`)
.then((res) => res.json)
.then((response) => {
setRowCount(response.data.count); //number of results!
return response.data.items.map(({ title }) => ({
label: title,
value: title,
}));
})
.then((formattedData) => setItems((prev) => [...prev, formattedData])); //add new datas to the previous list
};
return (
<InfiniteLoader
isRowLoaded={isRowLoaded}
loadMoreRows={loadMore}
rowCount={rowCount}>
{({ onRowsRendered }) => (
<List
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={rowRenderer}
/>
)}
</InfiniteLoader>
);
}
Related
I am not able to implement onClick functionality on AsyncTypeahead to console log the user ID after I find the user. can someone please help. thanks
const SEARCH_URI = 'https://api.github.com/search/users';
const AsyncExample = () => {
const [isLoading, setIsLoading] = useState(false);
const [options, setOptions] = useState([]);
const handleSearch = (query) => {
setIsLoading(true);
fetch(`${SEARCH_URI}?q=${query}+in:login&page=1&per_page=50`)
.then((resp) => resp.json())
.then(({ items }) => {
const options = items.map((i) => ({
avatar_url: i.avatar_url,
id: i.id,
login: i.login,
}));
setOptions(options);
setIsLoading(false);
});
};
const filterBy = () => true;
return (
<AsyncTypeahead
filterBy={filterBy}
id="async-example"
isLoading={isLoading}
labelKey="login"
minLength={2}
onSearch={handleSearch}
options={options}
placeholder="Search for a Github user..."
/>
);
};
Try using onChange, which fires after a menu option has been selected:
<AsyncTypeahead
...
onChange={(selected) => {
console.log(selected[0]?.id);
}}
/>
Note that selected is always an array.
have some problem, do little pokedex, have after chose the cound of cards on the page i need to reload a container with cards, can u help me?
To get selected item use onSelect,
handleSelect = (e) => {
this.setState({value:e})
}
<DropdownButton
variant="danger"
alignRight
id="dropdown-menu-align-right"
onSelect={this.handleSelect}>
and get it to link in component Pokemon list
<div className="col">
<PokemonList pages={this.value} />
</div>
PokemonList working like this
function PokemonList({ pages }) {
const [pokemonList, setPokemonList] = useState([]);
const [currPage, setCurrPage] = useState(
`https://pokeapi.co/api/v2/pokemon?offset=0&limit=${pages}`
);
const [nextPage, setNextPage] = useState();
const [prevPage, setPrevPage] = useState();
const [pageNum, setPageNum] = useState(0);
useEffect(() => {
let cancel;
axios
.get(currPage, {
cancelToken: new axios.CancelToken((c) => (cancel = c)),
})
.then((res) => {
setPokemonList(res.data.results);
setPrevPage(res.data.previous);
setNextPage(res.data.next);
})
.catch((error) => {
console.log(error);
});
return () => {
cancel();
};
}, [currPage, pageNum]);
i don't know but after select item at deop down, nothing changes, can u please help me
Find the problem, i tried to change only value, but i should to change the link, so answer was:
const handleChange = (e) => {
setCurrPage(`https://pokeapi.co/api/v2/pokemon?offset=${pageNum}&limit=${e}`);
};
The problem is that you try to store props inside state and with useEffect listen on state change. Because of this React can't properly update components. The currPage state doesn't change when pages change. You should avoid this because it's anti-pattern.
Working short example:
const Component = ({pages}) => {
const [pokemonList, setPokemonList] = useState([]);
useEffect( () => {
fetch("/api/pokemons/" + pages )
.then( res => res.json() )
.then( data => setPokemonList(data.pokemonList ))
.catch( err => console.log("handle errors") )
})
return <div>{ pokemonList.map( pokemon => <div>{ pokemon.name }</div>) }</div>
}
I want to implement something like infinity scrolling on my application. This is my code:
import React, { useState, useEffect } from "react";
import AsyncSelect from "react-select/async";
const WithPromises = () => {
const [page, setPage] = useState(1);
const [allData, setAllData] = useState([]); //here should be added all data
const [scrolll, setScrolll] = useState();
const filterData = (inputValue) => {
const req = fetch(
`https://jsonplaceholder.typicode.com/todos?_limit=15&_page=${page}`
)
.then((response) => response.json())
.then((res) => {
const fetchedData = res.map(({ title }) => {
return {
label: title,
value: title
};
});
page > 1 && setAllData([...allData, ...fetchedData]);
return [...fetchedData, allData];
});
return req;
};
const promiseOptions = (inputValue) => {
return filterData(inputValue);
};
const scroll = (e) => {
console.log(e);
setScrolll(e);
promiseOptions();
};
useEffect(() => {
setPage(page + 1);
}, [scrolll, page]);
return (
<AsyncSelect
cacheOptions
onMenuScrollToBottom={scroll}
isClearable={true}
isSearchable={true}
defaultOptions
loadOptions={promiseOptions}
/>
);
};
export default WithPromises;
How you can see, i increment the page when the scroll section is at the bottom: setPage(page + 1);. This value is added in the api https://jsonplaceholder.typicode.com/todos?_limit=15&_page=${page}.
Also i want to concat all data from each scroll here: return [...fetchedData, allData]. At the end i need to achieve something like this:
User scrolls down and in the scroll section are added mew values, but previous should't dissapear, so on every scrolling when the scroll bar is at the bottom the new data should be added at the bottom of the select.
Issue: I can't achieve what i described above and i don't know the issue.
Question: How to solve the issue in my situation?
demo: https://codesandbox.io/s/codesandboxer-example-forked-zsj6i?file=/example.js:0-1262
import React, { useState, useEffect } from "react";
import AsyncSelect from "react-select/async";
const WithPromises = () => {
const [page, setPage] = useState(1);
const [items, onItemsChange] = useState([]);
useEffect(() => {
fetchData(page);
}, [page]);
const fetchData = async (pageIdx) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos?_limit=15&_page=${pageIdx}`
).then((r) => r.json());
const resItems = res.map(({ title }) => ({
label: title,
value: title
}));
onItemsChange([...items, ...resItems]);
};
const loadOptions = () => items;
// for search functionality use something like this:
// const loadOptions = async (inputStr) => {
// const searchRes = await fetch(`${yourUrl}?search=${inputStr}`).then(r => r.json());
// return searchRes;
// };
const handleBottomScroll = () => {
setPage((prevVal) => prevVal + 1);
};
return (
<AsyncSelect
cacheOptions
isClearable={true}
isSearchable={true}
defaultOptions={items}
onMenuScrollToBottom={handleBottomScroll}
loadOptions={loadOptions}
/>
);
};
export default WithPromises;
My Functional component is as follows:
const Scratch = () => {
const [isLoaded, setIsLoaded] = useState(false);
const colorSelectItems=[];
const [selectedColor, setSelectedColor] = useState("fffff");
useEffect(() => {
fetch(
`http://localhost:8765/fetchData?userId=1`
)
.then((response) => response.json())
.then((data) => {
createDropDown(data));
setIsLoaded(true);
});
}, []);
const createDropDown= (data) => {
data.map((color) => {
colorSelectItems.push({
label: color.colorName,
value: color.hexValue,
});
});
return (
<div className="commonMargin">
{!isLoaded&& <p>Loading..</p>}
{isLoaded&& (
<Dropdown
value={selectedColor}
optionLabel="label"
options={colorSelectItems}
onChange={(e) => setSelectedColor(e.target.value);}
/>
)}
</div>
);
};
export default Scratch;
The problem is, it is displaying Loading... until the API call is complete, and it is rendering DropDown after that. But even after the completion of API call, the DropDown is still empty!
What am I missing here?
PS: This DropDown works perfectly if I replace fetching data from API to fetching data from local json file
Try this .In case any problem plz reply
const Scratch = () => {
const [isLoaded, setIsLoaded] = useState(false);
const colorSelectItems=[];
const [selectedColor, setSelectedColor] = useState("fffff");
useEffect(() => {
fetch(
`http://localhost:8765/fetchData?userId=1`
)
.then((response) => response.json())
.then((data) => {
var temp=data?.map((item)=>({label: item?.colorName,
value: item?.hexValue }));
colorSelectItems=temp;
setIsLoaded(true);
});
}, []);
return (
<div className="commonMargin">
{!isLoaded&& <p>Loading..</p>}
{isLoaded&& (
<Dropdown
value={selectedColor}
optionLabel="label"
options={colorSelectItems}
onChange={(e) => setSelectedColor(e.target.value);}
/>
)}
</div>
);
};
export default Scratch;
I have a map that render few items and i need when one element from map slected modal should load data about this selected items' id inside modal.
Like that:
<ListGroup>
{userinfo.map(item =>
(
<>
<ListGroup.Item key={item.id} onClick={handlePassInfoShow}>
{item.name}</ListGroup.Item>
</>
)
)}
</ListGroup>
<ModalPassInfo
modelClose={() => handlePassInfoClose()}
modelShow={showPaaInfo}
//id={item.id}
setshowPaaInfo={setshowPaaInfo}
/>
Here I am mapping through the user's array and adding a listgroup item to each of them with onClick modal. Now, whenever something is clicked inside map, the modal should be opened and read data about selected item.
And my modal like that.
const ModalPassInfo = ({modelShow, modelClose, id, showPaaInfo}) => {
const ref = React.createRef();
const [isError, setError] = useState(false);
const [isLoading, setLoading] = useState(true);
const [country_list, setCountries] = useState([]);
const [message, setMessage] = useState("");
const [data, setData] = useState({
//data about user
});
useEffect(() => {
loadNetwork();
}, []);
const loadNetwork = () => {
setLoading(true);
setError(false);
const selector = api.getItems("selector", {
tables: "country_list"
}).then(res => {
let response = res.data;
setCountries(response.country_list);
});
const data = api.getItems(`user-info/${id}`, {
}).then(res => {
let response = res.data;
setData(response);
});
Promise.all([selector, data]).then(res => {
console.log(res);
setError(false);
setLoading(false);
}).catch(e => {
console.log(e);
setMessage(e.toString());
setLoading(false);
setError(true);
});
};
const onRefresh = () => {
loadNetwork();
};
if (isError) {
return <ErrorMessage message={message} onRefresh={onRefresh}/>
}
if (isLoading) {
return <Loader/>
}
If I go to the page, the modal is loading immediately. And during onClick, only the last item id is retrieved.
And moy consts
const [showPaaInfo, setshowPaaInfo] = useState(false);
const handlePassInfoClose = () => setshowPaaInfo(false);
const handlePassInfoShow = () => {
setshowPaaInfo(true)
};
My question is. Any item on the map should send an id to the modal when the item is clicked. Where am I wrong?
Define one state
const [show, setShow] = React.useState(false);
function
const handlePassInfoShow = (data){
setShow(true);
console.log(data);
}
Change this to
<ListGroup>
{userinfo.map(item =>
(
<>
<ListGroup.Item key={item.id} onClick={()=>handlePassInfoShow(item)}>
{item.name}</ListGroup.Item>
</>
)
)}
</ListGroup>
{show && ( <ModalPassInfo
modelClose={() => handlePassInfoClose()}
modelShow={showPaaInfo}
//id={item.id}
setshowPaaInfo={setshowPaaInfo}
/>
)}