Array search returns a new empty array - javascript

Below is my code with a search input hoos and I can't identify why it isn't working.
import Herois from './json/videos.json'
function App() {
const [valueInput, setValueInput] = useState('')
const [newArray, setNewArray] = useState([])
useEffect(() => {
const results = Herois.filter((i) => {
i.title.toLowerCase().includes(valueInput.toLowerCase())
})
setNewArray(results)
console.log(newArray)
}, [valueInput])
}
is always becoming an empty array

const results = Herois.filter((i) => {
// you have to return the something here
return i.title.toLowerCase().includes(valueInput.toLowerCase())
})
or
const results = Herois.filter((i) => (i.title.toLowerCase().includes(valueInput.toLowerCase())
))

Related

array wont render on React state change

I have this useState hook:
const [products, setProducts] = useState([])
and I have these functions:
const sortByLow =()=>{
const newArray = products.sort((a,b)=>b.price-a.price)
setProducts(newArray)
console.log(newArray);
}
const sortByHigh =()=>{
const newArray = products.sort((a,b)=>a.price-b.price)
setProducts(newArray)
console.log(newArray);
}
a useEffect hook:
useEffect(()=>{
const displayProducts = async()=>{
try {
//fetch from server at port 3000
const response = await fetch('http://localhost:3000/')
if(!response.ok){
throw new Error("displayProducts response is not ok")
}
const responseDataObject = await response.json()
const allProducts = responseDataObject.data.allProducts
setProducts(allProducts);
} catch (error) {
console.log("theres an error" + error);
}
}
//call the function, duh
displayProducts();
}, [])
and the return value of the component is this:
<div>
{products.filter( product => {return (product.price > lowPrice && product.price < highPrice)} ).map(productObj => <ProductComponent
navigateToProduct = {productObj._id}
navigateToCategory = {productObj.category}
key = {productObj._id}
name = {productObj.name}
category = {productObj.category}
price = {productObj.price}
description = {productObj.description}
image = {productObj.image}
/>)}
</div>
now I expect the product array to change according to the functions above but it wont happen for some reason.
what can be the problem? please help me
thanks!
ok I figured it out thanks to #KonradLinkowski comment... The sort only references the original array, so in order to create a new array I should have written [...products] as the source array, as follows:
const sortByLow =()=>{
const newArray = [...products].sort((a,b)=>b.price-a.price)
setProducts(newArray)
console.log(newArray);
}
const sortByHigh =()=>{
const newArray = [...products].sort((a,b)=>a.price-b.price)
setProducts(newArray)
console.log(newArray);
}
Thanks to all who read and helped!
When you sort through an array it does not make a new reference ID so it does not know to update the state. This is how you can force it to make a new reference
const sortByLow = () => {
const newArray = [...products];
newArray.sort((a, b) => b.price - a.price);
setProducts(newArray);
console.log(newArray);
};
const sortByHigh = () => {
const newArray = [...products];
newArray.sort((a, b) => a.price - b.price);
setProducts(newArray);
console.log(newArray);
};
This should update the react state

Set interval on component mount is getting too fast and is not spreading my array

Goal is to display a real time log that comes from an async function ( this func will return the last log ) and is updated each second. After that i want to accumulate the results on an array and if it get bigger than 5 i want to remove the first element.
Expected: A list of 5 items displayed on screen that is updated each second.
Results: Array is not accumulating above 2 items and the update is random and fast
code ->
const [logs, setLogs] = useState([])
const getLogs = async () => {
const lastLog = await window.getAppLogs()
if (logs.length > 5) {
// here i tried this methods ->
// reduceLogs.shift()
// reduceLogs.splice(0, 1)
const reduceLogs = [...logs, lastLog ]
delete reduceLogs[0]
return setLogs(reduceLogs)
}
const test = [...logs, lastLog] // this line is not accumulating above 2
setLogs(test)
}
useEffect(() => {
setInterval(getLogs, 1000);
}, [])
Updating state in setinterval needs to use the => syntax. So it should be -
setLogs(prevState => [...prevState.slice(-4), lastLog])
Plus you need to clear the interval as well. I've made a demo to display last 5 users when they are updated every second (from api).
export default function App() {
const [users, setUsers] = useState([
"michael",
"jack",
]);
const getUser = async () => {
const response = await fetch("https://randomuser.me/api/?results=1");
const user = await response.json();
return user;
};
const getUsers = async () => {
const user = await getUser();
setUsers(prevState => [...prevState.slice(-4), user.results[0].name.first]);
};
useEffect(() => {
const handle = setInterval(getUsers, 1000);
return () => {
clearInterval(handle);
};
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{users.map((log) => (
<div>{log}</div>
))}
</div>
);
}
Update: This should be your getLogs function, no if check, no return etc.
const getLogs = async () => {
const lastLog = await window.getAppLogs()
setLogs((prevState) => [...prevState.slice(-4), lastLog]);
}
hey AG its almost working perfectly, updates are constant now and it is accumulating. But is still not reducing length of the array. Its like u show me:
const [logs, setLogs] = useState([])
const getLogs = async () => {
const lastLog = await window.getAppLogs()
if (logs.length > 4) {
return setLogs((prevState) => [...prevState.slice(-4), lastLog])
}
setLogs((prevState) => [...prevState, lastLog])
}
useEffect(() => {
const handle = setInterval(getLogs, 2500);
return () => {
clearInterval(handle);
};
}, [])

Return arrays from custom hook

I created a custom hook that takes a string and 3 arrays of Users, called useSearchUsers
import { useCallback } from 'react';
export default function useSearchUsers() {
const searchUsers = useCallback((searchValue, assistants, interested, rejected) => {
const assistantResults = [];
const interestedResults = [];
const rejectedResults = [];
if (searchValue !== '') {
if (assistants.length > 0) {
assistants.forEach(user => {
if (user.assistantName.includes(searchValue)) {
const name = user.assistantName;
const image = user.assistantImage;
const id = user.assistantId;
assistantResults.push({
id,
name,
image
});
}
});
}
if (interested.length > 0) {
interested.forEach(user => {
if (user.interestedName.includes(searchValue)) {
const name = user.interestedName;
const image = user.interestedImage;
const id = user.interestedId;
interestedResults.push({
id,
name,
image
});
}
});
}
if (rejected.length > 0) {
rejected.forEach(user => {
if (user.rejectedName.includes(searchValue)) {
const name = user.rejectedName;
const image = user.rejectedImage;
const id = user.rejectedId;
rejectedResults.push({
id,
name,
image
});
}
});
}
}
}, []);
return searchUsers;
}
And on the screen where I want to call that hook I have a TextInput where the user can write a string. Here I have declared the assistants, interested and rejected arrays, but for obvious reasons I'm ommiting them here
import useSearchUsers from '../../hooks/assistance/useSearchUsers';
const [eventAssistants, setEventAssistants] = useState([]);
const [eventInterested, setEventInterested] = useState([]);
const [eventRejected, setEventRejected] = useState([]);
const [searchText, setSearchText] = useState('');
const searchUsers = useSearchUsers();
export default function Screen(props) {
return (
<Container>
<SafeAreaProvider>
<TextInput onChangeText={text => setSearchText(text)} />
<Button onPress={() => useSearchUsers(searchText, eventAssistants, eventInterested, eventRejected)} />
</SafeAreaProvider>
</Container>
);
}
My question is, how can I return the 3 results arrays from the hook and passing them to the screen?
You can return an array containing the 3 result arrays.
return [assistantResults, interestedResults, rejectedResults];
instead of return searchUsers;
And to consume it in Screen you can destructure the hook's return value.
const [assistantResults, interestedResults, rejectedResults] = useSearchUsers();

having n states in react, assuming that n won't be received in props

How could I have n states in a React component
Assuming that the component won't receive this n value in any props, is something that it will get from a database
Using useState will create the state, setState for each pair, but I need n pairs
Rafael
JavaScript arrays doesn't have a fixed length.
You can do something like
const [arr, setArr] = useState([]);
And when you receive n values from database just set it to the array using setArr(values)
Now arr will be an array containing n elements retrieved from database. You can then iterate over it and render them as you wish.
As T J pointed out. You can use an array in state.
Or, another option is to map n Components for each item, therefore instantiating n states.
const Example = (props) => {
const [data, setData] = useState();
useEffect(() => {
// ...fetch data
// setData(data);
});
if (data === undefined) {
return null;
}
return data.map((data) => <Item data={data} />);
};
const Item = (props) => {
const [state, setState] = useState(props.data);
return <>Example</>;
};
Or if n is literally just a number, a count. Then you could do something like this.
const Example = (props) => {
const [count, setCount] = useState();
useEffect(() => {
// ...fetch count
// setCount(count);
});
if (count === undefined) {
return null;
}
const items = [];
for (var i = 1; i <= count; i++) {
items.push(<Item />);
}
return items;
};
const Item = (props) => {
const [state, setState] = useState();
return <>Example</>;
};

How to show all my arrays with Firebase and React?

I've some pushes in Firebase, and I've the data. I've separate the arrays side by side, and I want to show they. But, with my code, I just have the last array on my all arrays.
How to show all the arrays side by side ?
My code :
// useEffect()
let postJSON
firebase.database().ref('plugins/posts/').on('value', (snapshot) => {
const json = snapshot.toJSON()
for (const i in json) {
const element = json[i]
postJSON = [element.name, element.description, element.price, element.linkPlugin]
console.log(postJSON)
setPost(postJSON.map((x, i) => <p key={i}>{x}</p>))
}
})
// Render
return (
{post}
)
try
const MyComponent = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
firebase.database().ref('plugins/posts/').on('value', (snapshot) => {
const json = snapshot.toJSON()
const keys = Object.keys(json);
const postJSON = keys.map(key => {
const element = json[key];
return [element.name, element.description, element.price, element.linkPlugin]
});
setPosts(postJSON);
})
}, []);
return (
<div>{posts.map((x, i) => <p key={i}>{x}</p>)}</div>
)
}
as is you are calling setPosts 3 times, once for each array item, each time overriding the previous call to setPosts. You need to just call it once with an array of arrays

Categories