I take datas from json file and there is so much data inside because of this I don't want to show all. In the page I want to show just 20 data and when I click NEXT button i want to see another 20 data. It's working but in the filter function it update filteredDatas but don't update showingDatas
import data from './data.json'
var i = 0
var j = 20
function App() {
const [showingDatas, setShowingDatas] = useState([])
const [filteredDatas, setFilteredDatas] = useState([])
const getDatas = () =>{
setFilteredDatas(data)
setShowingDatas(data.slice(0,20))
}
useEffect(()=>{
getDatas()
},[])
const next = () => {
i += 20;
j += 20;
setShowingDatas(filteredDatas.slice(i,j))
}
const filter = () => {
setFilteredDatas(data.filter(item => item.productCompany === "Mavi"))
i = 0
j = 20
setShowingDatas(filteredDatas.slice(0,20))
}
You can not do this like that, because of React working principles. But if you want to do that I suggest you use useEffect to keep filteredData changes, then in useEffect set it. Like:
useEffect(() => {
setShowingDatas(filteredDatas)
}, [filteredDatas]);
If you really want to do with useState I suggest you search useState with callback.
Related
In my React app I am showing a banner yes or no, based on React state and some values set in localStorage.
After close button is clicked, it's state showBanner is saved to localStorage and doesn't show the banner anymore
After 2 times using a page url in the React app with query param redirect=my-site it doesn't show the banner anymore:
import queryString from 'query-string';
const location = useLocation();
const queryParams = queryString.parse(location.search);
const [showBanner, setShowBanner] = useState(true);
const handleClick = () => {
setShowBanner(false);
localStorage.removeItem('redirect');
};
const hasQp = queryString
.stringify(queryParams)
.includes('redirect=my-site');
const initialCount = () => {
if (typeof window !== 'undefined' && hasQp) {
return Number(localStorage.getItem('redirect')) || 0;
}
return null;
};
const [count, setCount] = useState(initialCount);
const show = showBanner && hasQp && count! < 3;
useEffect(() => {
const data = localStorage.getItem('my-banner');
if (data !== null) {
setShowBanner(JSON.parse(data));
}
}, []);
useEffect(() => {
localStorage.setItem('my-banner', JSON.stringify(showBanner));
}, [showBanner]);
useEffect(() => {
let pageView = count;
if (pageView === 0) {
pageView = 1;
} else {
pageView = Number(pageView) + 1;
}
if (hasQp && showBanner === true) {
localStorage.setItem('redirect', String(pageView));
setCount(pageView);
}
}, []);
This is working fine (when you see some good code improvements let me know :) ).
But as soon the user clicks the close button I don't want the localStorage item redirect no longer appears. Now after refreshing the page it appears again.
How do i get this to work?
If this is executing when the page loads:
localStorage.setItem('redirect', String(pageView));
Then that means this is true:
if (hasQp && showBanner === true)
The hasQp value is true, which means this is true:
const hasQp = queryString
.stringify(queryParams)
.includes('redirect=my-site');
And showBanner is true because it's always initialized to true:
const [showBanner, setShowBanner] = useState(true);
It's not 100% clear to me why you need this state value, but you could try initializing it to false by default:
const [showBanner, setShowBanner] = useState(false);
But more to the point, I don't think you need this state value at all. It's basically a duplicate of data that's in localStorage. But since both state and localStorage are always available to you, I don't see a reason to duplicate data between them.
Remove that state value entirely and just use localStorage. An example of checking the value directly might be:
if (hasQp && JSON.parse(localStorage.getItem('my-banner')))
Which of course could be refactored to reduce code. For example, consider a helper function to get the value:
const showBanner = () => {
const data = localStorage.getItem('my-banner') ?? false;
if (data) {
return JSON.parse(data);
}
};
Then the check could be:
if (hasQp && showBanner())
There are likely a variety of ways to refactor the code, but overall the point is to not duplicate data. In thie case a value is being stored in localStorage instead of React state because it needs to persist across page loads. Just keep that value in localStorage and use it from there.
I have the following code in React. This code uses a time expensive function that performs some coputations over a large number of data. in the begining the state is filled with an array that says no computations. When the button is pressed, I want that array of the state to be filled with waiting, and after each iteration over i is complete, the element on potition i to be 'Done'. The problem is that by using set timeout, the DOM elements are not updating untill the whole function is over. Ignore the computations in the for, they are meaningless in this example. To have an ideea the function performs polynomial fit over the given data. My problem is how can I update the Dom elements while the function is running, So i can knwow which data is done and which is computing in real time
const Component = ( {data1, data2} ) => {
const [state, setState] = useState(Array(data1.length).fill('No Computations'))
const timeExpensiveFunction = () => {
// data1.length = 10
let variable0
let variable1
let variable2
let variable3
for(let i = 0; i< data1.length; i++){
//data1[i].length = 40 000
for(let j = 0; j< data1[i].length; j++){
variable1 += data1[i][j]
variable2 += variable1*data2[i][j]**3
variable3 += data2[i][j]**2
variable1 += data1[i][j]*data2[i][j]
}
setTimeout(() => {
setState(state => {
return[
...state.slice(0,i),
'Done',
...state.slice(i+1),
]
})
},1000)
}
return([variable1,variable2,variable3,variable4])
}
const randomFunc = (e) => {
setTimeout((e) => setState(Array(data1.length).fill('Waiting ...')),1000)
timeExpensiveFunction() //Duration depends on the number of data, it is usually higher that 10-15 seconds
}
return (
<div>
<div>
{state.map((element) => (
{element} //This does not update with the function setTimeout. It sets after the expensive computation is over
))}
</div>
<div>
<button onClick = {(e) => randomFunc(e)}>press</button>
</div>
</div>
)
}
First of all, You must use useState and useEffect to set a state and re-render the DOM.
You can use like below:
import { useState, useEffect } from 'react'
const Component = () => {
const [state, setState] = useState([1,2,3,4,5,6,7,8,9,10])
const [isPress, setPress] = useState(false); // use it to not call a method on first render
const timeExpensiveFunction = () => {
// lot of work here
}
const randomFunc = (e) => {
setState([10,9,8,7,6,5,4,3,2,1])
setPress(true)
}
useEffect({
if (isPress) setTimeout(() => timeExpensiveFunction(),1000)
}, [state])
return (
<div>
<div>
{state.map((element) => (
{element}
))}
</div>
<div>
<button onClick = {(e) => randomFunc(e)}>press</button>
</div>
</div>
)
}
You need an input custom that limits you to 10 commas.
When it is 10, it should be possible to modify it.
Currently, the input event works faster, so additional commas are added so it cannot be edited.
my code
// input props
value={relationSearch}
onChange={(e) => handleOnChangeInput(e)}
onKeyPress={handleKeyPress}
//callbackfunction
const relationCallbackFunction = {
handleOnChangeInput: (e) => {
setRelationSearch(e.target.value);
},
};
// input event area
const handleOnChangeInput = (e) => {
relationCallbackFunction.handleOnChangeInput(e);
};
const handleKeyPress = (e) => {
const currentStr = relationSearch.split(',');
console.log(e.target.value);
if (currentStr.length > 11) {
e.target.value ='';
}
};
you could do this a couple of ways, but here is a simple solution. The important part here is to ensure you set your input to a controlled input, where you provide the value from react.
We need a function which will implement your logic. It should take in a string and the total number of commas you want, and limit that string to the comma amount.
Below is a very simple function that does this. It splits the string using commas, ensures the result array stays at 10 length, and returns a joined string from the resultant array.
function ensureCommas(str, commas = 10) {
const commaArray = str.split(',');
const reduced = commaArray.slice(0, commas);
return reduced.join(',');
}
Now to use it. Here is a very simple App component which keeps the input value in state and provides this state value to the input, and has an onChange event handler which calls the above function on every key press
import { useState } from "react";
import "./styles.css";
function ensureCommas(str, commas = 10) {
const commaArray = str.split(",");
return commaArray.slice(0, 10);
}
export default function App() {
const [value, setValue] = useState("");
const onInputChange = (e) => {
const inputVal = e.target.value;
const newInputVal= ensurecommas(inputVal , 10);
setValue(newInputVal);
};
return (
<div className="App">
<input value={value} onChange={onInputChange}></input>
</div>
);
}
CodeSandbox
Remove the handleKeyPress function from the input props and update the relationCallbackFunction object.
const relationCallbackFunction = {
handleOnChangeInput: (e) => {
let value = e.target.value;
const currentStr = value.split(",");
if (currentStr.length <= 10) {
setRelationSearch(value);
}
}
};
Purpose: I want to create any # of rows containing any # of checkboxes that will be handled by a useState hook.
Problem: Page becomes frozen / constant loading state with nothing showing. No console logs, and debugger doesn't even start. React usually will prevent endless loops of updates. But in this case it didn't get caught.
What I've tried:
console.logs (nothing gets outputted)
debugger statements (nothing
gets paused)
Cant do much bc of frozen page.
CODE:
const CreateCheckboxes = ({ rows, cols }) => {
const classes = useStyles()
const [checked, setChecked] = useState({})
const [isLoaded, setIsLoaded] = useState(false)
//temp initializer
let state = {};
//configures state based on unique checkbox name.
const handleChange = (e) => {
const value = {
...checked,
[e.target.name]: e.target.checked,
}
setChecked(value)
};
//Helper function
const createBoxes = (row, col) => {
let rowArr = [];
for (let i = 0; i < row; i++) {
let checkboxArr = []
for (let j = 0; i < col; j++) {
checkboxArr.push(
<Checkbox
name={`row-${i}-checkbox-${j}`} //unique identifier in the state.
checked={checked[`row-${i}-checkbox-${j}`]}
onChange={(e) => handleChange(e)}
/>
)
//store temp state so that react useState is given a list of initialized 'controlled' states.
//react deosnt like undefined states.
state[`row-${i}-checkbox-${j}`] = false;
}
rowArr.push(
<div className={classes.row}>
<Typography>{`Sound ${i}`}</Typography>
{/* JSX array */}
{checkboxArr}
</div>
)
}
// JSX array
return rowArr
}
//output as a jsx array of 'x row divs' contiaining 'y checkboxes'
const sequenceData = createBoxes(rows, cols)
useEffect(() => {
setChecked(state)
setIsLoaded(true)
}, [])
return isLoaded && (
<>
{sequenceData}
</>
);
}
Solution: Check your loop conditions. Inner loop set to i instead of j.
yes, but I think that component doesn't need to have state various.
I tried to create one. You can check it out following when you have time to see :)
https://codesandbox.io/s/stackoverflow-dynamic-checkboxes-5dh08
Solution: Check your loop conditions. Inner loop set to i instead of j.
I'm having this table view in React where I fetch an array from API and display it. When the user types something on the table search box, I'm trying to clear the current state array and render the completely new result. But for some reason, the result keeps getting appended to the current set of results.
Here's my code:
const Layout = () => {
var infiniteScrollTimeout = true;
const [apiList, setapiList] = useState([]);
//invoked from child.
const search = (searchParameter) => {
//Clearing the apiList to load new one but the console log after setApiList still logs the old list
setapiList([]); // tried setApiList([...[]]), copying the apiList to another var and emptying it and then setting it too.
console.log(apiList); //logs the old one.
loadApiResults(searchParameter);
};
let url =
AppConfig.api_url + (searchParameter || "");
const loadApiResults = async (searchParameter) => {
let response = await fetch(url + formurlencoded(requestObject), {
method: "get",
headers: headers,
});
let ApiResult = await response.json(); // read response body and parse as JSON
if (ApiResult.status == true) {
//Set the url for next fetch when user scrolls to bottom.
url = ApiResult.next_page_url + (searchParameter || "");
let data;
data = ApiResult.data;
setapiList([...data]);
}
}
useEffect(() => {
loadApiResults();
document.getElementById("myscroll").addEventListener("scroll", () => {
if (
document.getElementById("myscroll").scrollTop +
document.getElementById("myscroll").clientHeight >=
document.getElementById("myscroll").scrollHeight - 10
) {
if (infiniteScrollTimeout == true) {
console.log("END OF PAGE");
loadApiResults();
infiniteScrollTimeout = false;
setTimeout(() => {
infiniteScrollTimeout = true;
}, 1000);
}
}
});
}, []);
return (
<ContentContainer>
<Table
...
/>
</ContentContainer>
);
};
export default Layout;
What am I doing wrong?
UPDATE: I do see a brief moment of the state being reset, on calling the loadApiResult again after resetting the state. The old state comes back. If I remove the call to loadApiResult, the table render stays empty.
add apiList in array as the second parameter in useEffect
You need to use the dependencies feature in your useEffect function
const [searchParameter, setSearchParameter] = useState("");
... mode code ...
useEffect(() => {
loadApiResults();
... more code ...
}, [searchParameter]);
useEffect will automatically trigger whenever the value of searchParameter changes, assuming your input uses setSearchParameter on change