Instead of using fileList.map(), I want to put it in for loop and setTimeout(500) for every single loop. How can I do it ?
const SortableList = SortableContainer(({ fileList }) => (
<div>
{
fileList.map((file, index) => (
<SortableItem key={`item-${index}`} index={index} value={file} />
))
}
</div>
))
A very simple way is to create a new array which should contain the items which are currently visible.
Then do a simple setTimeout which will call a recursive function every 500ms that will add one element to the visible array.
const SortableList = SortableContainer(({ fileList }) => {
const [visibleItems, setVisibleItems] = useState([]);
useEffect(() => {
function displayNext(next) {
setVisibleItems(prevItems => [...prevItems, fileList[next]]);
if (++next < fileList.length) {
setTimeout(() => {
displayNext(next);
}, 500);
}
}
displayNext(0);
}, []);
return (
<div>
{
visibleItems.map((file, index) => (
<SortableItem key={`item-${index}`} index={index} value={file} />
))
}
</div>
)});
Let me know if works!
You can do something like this:
import React, {useState, useEffect} from 'react';
const ITEMS_TO_ADD_IN_EACH_TIMEOUT = 50
const SortableList = SortableContainer(({ fileList }) => {
const [visibleItems, setVisibleItems] = useState([])
useEffect(() => {
const timer = setTimeout(() => {
setVisibleItems([fileList.slice(0, visibleItems.length + ITEMS_TO_ADD_IN_EACH_TIMEOUT)])
}, 500);
return () => clearTimeout(timer);
})
return (
<div>
{
visibleItems.map((file, index) => (
<SortableItem key={`item-${index}`} index={index} value={file} />
))
}
</div>
))
}
Related
I've a problem with my react app.The navbar worked correctly before the ItemDetailContainer, and i want to show in the DOM an specific product. When adding the ItemDetailContainer to app.js, the page doesn't show anything (including the navbar) . Here's my code:
ItemDetailContainer.jsx:
const {products} = require('../utils/data');
const ItemDetailContainer = () => {
const [arrayList, SetArrayList] = useState({});
useEffect(() => {
customFetch(2000, products[0])
.then(result => SetArrayList(result))
.catch(err => console.log(err))
}, [])
return (
<div>
<ItemDetail products={arrayList}/>
</div>
);
}
here's my array in data.js that i want to access:
const products = [
{
id: 1,
image: "https://baltimore.com.ar/img/articulos/4188.png",
title: "Vino Abras Malbec 750cc",
price: 2.500,
stock: 8,
initial: 1
}
ItemDetail.jsx:
const ItemDetail = ({product}) => {
return(
<>
{product.image}
{product.title}
</>
)
}
CustomFetch.js:
let is_ok = true
let customFetch = (time, array) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (is_ok) {
resolve(array)
} else {
reject("error")
}
}, time)
})
}
and my app.js:
function App() {
return (
<div>
<Navbar/>
<ItemDetailContainer/>
</div>
);
}
I've done all the imports and exports, but I dont' know what's the problem.
Since you are passing a product prop in <ItemDetail product={arrayList}/> within ItemDetailContainer make sure you destructure your props correctly in ItemDetail component,like so :
const ItemDetail = ({product}) => {
return(
<>
{product.image}
{product.title}
</>
)
}
Which is basically like this :
const ItemDetail = (props) => {
return (
<>
{props.product.image}
{props.product.title}
</>
);
};
check it out in code sandbox
I have tried to create an autocomplete suggestion box from an Thailand's province database URL.
This is my source code. I export this to App.js in src directory
import React, { useEffect, useState, useRef } from "react";
const Test = () => {
const [display, setDisplay] = useState(false);
const [singleProvince, setSingleProvince] = useState([]);
const [singleProvinceData, setSingleProvinceData] = useState([]);
const [search, setSearch] = useState("");
const wrapperRef = useRef(null);
const province_dataBase_url = 'https://raw.githubusercontent.com/earthchie/jquery.Thailand.js/master/jquery.Thailand.js/database/raw_database/raw_database.json'
useEffect(() => {
const promises = new Array(20).fill(fetch(province_dataBase_url)
.then((res) => {
return res.json().then((data) => {
const createSingleProvince = data.filter( (each) => {
if (false == (singleProvince.includes(each.province))) {
setSingleProvince(singleProvince.push(each.province))
setSingleProvinceData(singleProvinceData.push(each))
}
})
return data;
}).catch((err) => {
console.log(err);
})
}))
}, [])
useEffect(() => {
window.addEventListener("mousedown", handleClickOutside);
return () => {
window.removeEventListener("mousedown", handleClickOutside);
};
});
const handleClickOutside = event => {
const { current: wrap } = wrapperRef;
if (wrap && !wrap.contains(event.target)) {
setDisplay(false);
}
};
const updateProvince = inputProvince => {
setSearch(inputProvince);
setDisplay(false);
};
return (
<div ref={wrapperRef} className="flex-container flex-column pos-rel">
<input
id="auto"
onClick={() => setDisplay(!display)}
placeholder="Type to search"
value={search}
onChange={event => setSearch(event.target.value)}
/>
{display && (
<div className="autoContainer">
{ singleProvinceData
.filter( ({province}) => province.indexOf(search.toLowerCase()) > -1)
.map( (each,i) => {
return (
<div
onClick={() => updateProvince(each.province)}
className="singleProvinceData"
key={i}
tabIndex="0"
>
<span>{each.province}</span>
</div>
)
})}
</div>
)}
</div>
);
}
export default Test
When click on an input box, the console says "TypeError: singleProvinceData.filter is not a function"
enter image description here
I cannot find out what's wrong with my code
The issue is with the "singleProvinceData" state is not set correctly.
you cannot push data directly into the state.
useEffect(() => {
const promises = new Array(20).fill(fetch(province_dataBase_url)
.then((res) => {
return res.json().then((data) => {
const shallowSingleProvinceList = [];
const shallowSingleProvinceDataList = [];
const createSingleProvince = data.filter( (each) => {
if (false == (singleProvince.includes(each.province))) {
shallowSingleProvinceList.push(each.province)
shallowSingleProvinceDataList.push(each)
}
})
setSingleProvince(shallowSingleProvinceList)
setSingleProvinceData(shallowSingleProvinceDataList)
return data;
}).catch((err) => {
console.log(err);
})
}))
}, [])
You can show the data conditionally
{display && (
<div className="autoContainer">
{ singleProvinceData && singleProvinceData
.filter( ({province}) => province.indexOf(search.toLowerCase()) > -1)
.map( (each,i) => {
return (
<div
onClick={() => updateProvince(each.province)}
className="singleProvinceData"
key={i}
tabIndex="0"
>
<span>{each.province}</span>
</div>
)
})}
</div>
)}
So here I have search functionality. Everything works fine except when an Item has not been found in the array. I have tried something with objects.Keys but it is displaying it on the render not when the Book has not been found like it should. Should I use if statement or.
import React,{useState, useEffect, useRef, useContext}from 'react'
import {FaSearch} from 'react-icons/fa'
import {
Link, useHistory
} from "react-router-dom";
import { BookContext } from '../../context/books';
import SearchBooks from './SearchBooks';
const Search = () => {
const {data}= useContext(BookContext)
const [searchValue, setSearchValue] = React.useState('');
const history= useHistory()
const ref=useRef()
function filterBooks(book) {
console.log(book);
if (!searchValue.length ) return false;
return book.bookName?.toLowerCase().includes(searchValue.toLowerCase());
}
const handleSearch = (e) => {
if (ref.current && !ref.current.contains(e.target)) {
setSearchValue('')
}
};
useEffect(() => {
document.addEventListener('click', handleSearch);
return () => {
document.removeEventListener('click', handleSearch);
};
}, []);
return (
<div className='search__cont' ref={ref}>
{Object.keys(data).filter(filterBooks).length === 0 &&(
<div>
<h3>Book not found</h3>
</div>
)}
<SearchBooks searchValue={searchValue} setSearchValue={setSearchValue }/>
{Object.keys(data)
.map((key) => data[key])
.reduce((acc, curr) => acc.concat(curr), [])
.filter(filterBooks)
.map((book) => {
return (
<>
<div className='search__books'
onClick={() => {
history.push("/book/id", { book }); setSearchValue('')
}}
>
{" "}
{book.bookName}{" "}
</div>
</>
);
})}
</div>
)
}
export default Search
You're filtering the category names instead of the books (data is an object with category names as keys and books as values). You can use Object.values and Array.prototype.flat to get an array of all the books and then apply the filter.
const filteredBooks = Object.values(data).flat().filter(filterBooks)
const searchQueryPresent = searchValue.trim().length > 0
{
searchQueryPresent &&
(filteredBooks.length === 0 ? (
<div>No books found</div>
) : (
filteredBooks.map((book) => {
return <>{/* render books */}</>
})
))
}
I have a state
const [ideas, setIdeas] = useState([{title:"test", favourite:false]);
Component Idea.jsx returns props.title and a button "fav".
App.jsx maps through the idea[] and renders each idea.title in
<Item title = {idea.title}/>
on the page.
Problem:
Every time when "fav" is clicked I want to toggle ideas[index].favourite.
How to change a value of favourite only for an idea that was clicked?
How to add this exact idea to the array favourites[]?
App.jsx
function App() {
const [ideas, setIdeas] = useState([{title:"test",
favourite:false}]);
const [isClicked, setIsClicked] = useState(false)
function showAllIdeas () {
setIsClicked(prevValue => {
return !prevValue
}
)
}
function mapIdeas(){return ideas.map((ideaItem, index) => {
return (<Idea
key = {index}
id = {index}
title = {ideaItem.title}
/>
);
})}
return ( <div>
<Fab color="primary" onClick={showAllIdeas}>{expandText()}</Fab>
{isClicked && mapIdeas()}
</div>)
}
Item.jsx
function Idea(props) {
const [isClicked, setIsClicked] = useState(false)
function handleClick(){
setIsClicked(prevValue => {
return !prevValue
})
}
console.log(isClicked)
return(
<div className={"idea-list" } ><p>{props.title} {isClicked ?
<StarIcon onClick={handleClick}/> :<StarBorderIcon onClick=.
{handleClick}/>}</p>
</div>
)
}
const handleFavToggle = (index) => {
setItems(items=> {
const data = [...items]
data[index] = {...data[index],favourite: !data[index].favourite }
return data
})
}
<Item key={index} title={item.title} index={index} handleFavToggle={handleFavToggle}/>
In item component you have to handle click with handleFavToggle and pass all params
I'm making a filter function by checkbox. I made a reproduce like below. I changed values in array but checkbox checked status not change, what I missed? I think I must re-render list but it's also refill checked array to initial state. What should I do? Thanks!
import * as React from "react";
import "./styles.css";
import { Checkbox } from "antd";
const arr = [
{
name: "haha"
},
{
name: "haha2"
},
{
name: "hahaha"
}
];
const App = () => {
let [viewAll, setViewAll] = React.useState(true);
let checked = new Array(3).fill(true);
// render calendars checkbox
let list = arr.map((value, index) => {
return (
<Checkbox
style={{ color: "white" }}
checked={checked[index]}
onChange={() => handleFilter(value, index)}
className="check-box"
>
haha
</Checkbox>
);
});
const handleViewAll = () => {
setViewAll(!viewAll);
checked = checked.map(() => viewAll);
};
const handleFilter = (value, index) => {
setViewAll(false);
checked.map((_value, _index) => {
if (_index === index) {
return (checked[_index] = !checked[_index]);
} else {
return checked[_index];
}
});
console.log(checked);
};
return (
<div className="App">
<Checkbox checked={viewAll} onChange={() => handleViewAll()}>Check all</Checkbox>
{list}
</div>
);
};
export default App;
Here is codesanboxlink
You should create checked state. Check the code below.
let [viewAll, setViewAll] = React.useState(true);
let [checked, setChecked] = React.useState([true, true, true]);
// render calendars checkbox
let list = arr.map((value, index) => {
return (
<Checkbox
style={{ color: "black" }}
checked={checked[index]}
onChange={() => handleFilter(value, index)}
className="check-box"
>
{value.name}
</Checkbox>
);
});
const handleViewAll = () => {
setViewAll(!viewAll);
setChecked(() => checked.map(item => !viewAll));
};
const handleFilter = (value, index) => {
setViewAll(false);
setChecked(() =>
checked.map((_value, _index) => {
if (_index === index) return !checked[_index];
return checked[_index];
})
);
};
return (
<div className="App">
<Checkbox checked={viewAll} onChange={() => handleViewAll()}>
{checked}
</Checkbox>
{list}
</div>
);
codesandbox demo
You have to define checked array as a state value.
Right now your code is not firing render function because checked array is not props but also not state.