I am populating an array with dummy data and once it is done, using useEffect, i'm trying to delete the the element on first index and update it on the DOM. The array is being updated on console but the changes doesn't reflect on DOM.
import './App.css';
import { useState,useEffect } from 'react';
import {faker} from '#faker-js/faker'
function App() {
var catsarray = []
const [cats, changecat] = useState(()=>{
for (let index = 0; index < 10; index++) {
catsarray.push(faker.animal.cetacean())
}
return catsarray
})
useEffect(()=>{
},[cats])
const removecat = () => {
cats.shift()
changecat(cats)
}
return (
<div className="App">
<h1> List of cats </h1>
<div className='wrapper'>
<ul>
<>
{catsarray.length > 2 ?
cats.map((title, index)=>(
<li key={index}> {index} : {title} </li>
)) : 'Array is empty' }
</>
</ul>
</div>
<button title='button' onClick={removecat}> Click me </button>
</div>
);
}
export default App;
The reason is like #Jacob Smit said:
React uses referential comparisons to determine whether or not a value has changed. If you mutate (change) the state and provide it back to the setState function react sees the same value as it already has stored and thinks there is no operation to be carried out
So you need to use a different refernece when mutating a state
for example
// assign a new array
let tempCats = [...cats]
// operate this new array
tempCats.shift()
// set the new array to state
changecat(tempCats)
Or like #Jacob Smit's suggestion
changecat(cats => cats.filter((_, i) => i !== 0))
a example from your question with a little modify
import React from "react"
import { useState, useEffect } from 'react';
function App() {
var faker = ["steve", "john", "cat", "dog", "alex", "big"]
var catsarray = []
const [cats, changecat] = useState(() => {
for (let index = 0; index < faker.length; index++) {
catsarray.push(faker[index])
}
return catsarray
})
useEffect(() => {
}, [cats])
const removecat = () => {
let tempCats = [...cats]
tempCats.shift()
console.log(tempCats)
changecat(tempCats)
// or #Jacob Smit's suggestion
//changecat(cats => cats.filter((_, i) => i !== 0))
}
return (
<div>
<h1> List of cats </h1>
<div>
<ul>
<>
{cats.length > 2 ?
cats.map((title, index) => (
<li key={index}> {index} : {title} </li>
)) : 'Array is empty'}
</>
</ul>
</div>
<button title='button' onClick={removecat}> Click me </button>
</div>
);
}
export default App;
a code snippet display
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function App() {
var faker = ["steve", "john", "cat", "dog", "alex", "big"]
var catsarray = []
const [cats, changecat] = React.useState(() => {
for (let index = 0; index < faker.length; index++) {
catsarray.push(faker[index])
}
return catsarray
})
React.useEffect(() => {
}, [cats])
const removecat = () => {
let tempCats = [...cats]
tempCats.shift()
console.log(tempCats)
changecat(cats => cats.filter((_, i) => i !== 0))
}
return (
<div>
<h1> List of cats </h1>
<div>
<ul>
{cats.length > 2 ?
cats.map((title, index) => (
<li key={index}> {index} : {title} </li>
)) : 'Array is empty'}
</ul>
</div>
<button title='button' onClick={removecat}> Click me </button>
</div>
);
}
</script>
<script type="text/babel">
ReactDOM.render(
<App></App>
, document.getElementById("root"));
</script>
Related
Updated code: Im trying to first display carsList and only when selectedMake is selected, I would update the state with the result from filter and show another array. I tried storing carsList in updatedCarsList so it has all the cars on page load but Im missing something here.
CarOffers.jsx
const CarOffers = () => {
const [carsList, setCarsList] = useState([]);
const [updatedCarsList, setUpdatedCarsList] = useState([]);
const [selectedMake, setSelectedMake] = useState(undefined);
const getCars = () => {
axios.get(url)
.then((response) => {
return setCarsList(response.data)
})
}
const handleMakeChange = (select) => {
setSelectedMake(select.value)
}
const applyFilters = () => {
let updatedCarsList = carsList
if(selectedMake) {
updatedCarsList = carsList.filter(car => car.make === selectedMake)
setUpdatedCarsList(updatedCarsList);
} else {
setUpdatedCarsList(carsList)
}
}
useEffect(() => {
getCars()
applyFilters()
}, [ selectedMake ]);
return (
<div className="mka__wrapper-car-offers">
<div className="mka__container">
<div className="mka__content-car-offers">
<div className="mka__content-grid-offers">
<div className="item1">
< CarSlider/>
<div className="mka-responsive-item">
< DisplayCars/>
< SortingCars/>
< CarAlignment/>
</div>
</div>
<div className="item2">
<div className="mka__side-bar-divider">
< Search
carsList={carsList}/>
</div>
<div>
< FilterSideBar
carsList={carsList}
handleMakeChange={handleMakeChange} />
</div>
</div>
<div className="item3">
<Cars updatedCarsList={updatedCarsList}/>
</div>
</div>
</div>
</div>
</div>
)
}
export default CarOffers;
Cars.jsx
const Cars = ({ updatedCarsList }) => {
return (
<div className='mka__cars-grid'>
{updatedCarsList.map(car =>
<CarsItem key={car.id} car={car}/>)}
</div>
)
}
export default Cars
CarItem.jsx
const CarsItem = ({car: {year,month,transmission,mileage,price,title,link}}) => {
return (
<Fragment>
<div className="cars-item_wrapper">
<div className="cars-item_image">
<img src={link} alt="car" />
</div>
<div>
<a
className="cars-item_car-title"
href="/"
>
{title}
</a>
</div>
<div className=" cars-item_separator"></div>
<p className="cars-item_car-text">{price}</p>
</div>
</Fragment>
)
}
export default CarsItem
Move your applyFilters above getCars
Does Select need to be in <>
distinctBy... urgh.. use Set const unique = [...new Set(data.map(item => item.value))]
applyFilters... axios is async, but your setting a value so state doesn't update so no re-render? Maybe.
selectedMake - don't use null as a default, use undefined.
Hope that helps, feels like a state management issue.
... think its this ....
You are using carsList as your list of cars, however you are setting the value of carsList with setCarsList(updatedCarsList)... updatedCarsList is a filtered list of cars... only car => car.make === selectedMake so once you've selected a make your carList is only cars with the selected make.
Solution is to
Either separate the list from the filtered list
or preferably keep list, but pass the filtered state to the component that needs it... but not update state of the original list by calling setCarsList(updatedCarsList);
if (selectedMake){
updatedCarsList = updatedCarsList.filter(
car => car.make === selectedMake
)
};
setCarsList(updatedCarsList);
I am trying to add a hint option to my quiz app.
If the 50/50 button is clicked I want to render the newAnswers array. else I want to render the shuffledAnswers array.
when I run this code I get
TypeError: Cannot read properties of null (reading 'useState')
What am I doing wrong here?
import React from "react";
import { useState } from "react/cjs/react.production.min";
import Card from "../UI/Card";
import "./DisplayQuestion.css";
import ProgressBar from "./Progress";
const DisplayQuestion = (props) => {
/*I used dangerouslySetInnerHTML to fix the quotes gibberish problem */
const [hint, setHint] = useState(false);
console.log("hint: ", hint);
let notBoolean = false;
const newAnswers = [
props.data.correct_answer,
props.data.incorrect_answers[0],
];
/*Helper functions */
// shuffles the answers
let shuffledAnswers = [
props.data.correct_answer,
...props.data.incorrect_answers,
].sort(() => Math.random() - 0.5);
if (shuffledAnswers.length > 2) {
notBoolean = true;
console.log("notBoolean");
}
const answersHintHandler = () => {
setHint(true);
console.log("hint: ", hint);
console.log(newAnswers);
};
let progress = Math.round((props.score / props.numOfQuestions) * 100);
return (
<div>
<h3 class="diff">Difficulty: {props.diff}</h3>
<div classname="Questions">
<Card>
<ProgressBar bgcolor="#99ccff" progress={progress} height={30} />
<h2>
Questions {props.index}/{props.numOfQuestions}
</h2>
<h2
className="question-text"
dangerouslySetInnerHTML={{
__html: props.data.question,
}}
/>
<ul>
{notBoolean ? (
<button onClick={answersHintHandler}>50/50</button>
) : (
<p></p>
)}
{hint
? newAnswers.map((answer) => {
return (
<li
onClick={() => props.handler(answer)}
dangerouslySetInnerHTML={{
__html: answer,
}}
></li>
);
})
: shuffledAnswers.map((answer) => {
return (
<li
onClick={() => props.handler(answer)}
dangerouslySetInnerHTML={{
__html: answer,
}}
></li>
);
})}
</ul>
</Card>
</div>
</div>
);
};
export default DisplayQuestion;
Replace 1st and 2nd line with
import React, { useState } from 'react'
You are importing useState from the wrong path x)
import { useState } from "react/cjs/react.production.min";
Care the automatic imports haha
I have StackBlitz Demo here
Simple question is it possible to show the first item in a map based on a conditional.
So in my example how could I just output the first item '1' if 'first' is true
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
const App = () => {
const list = ["1", "2", "3", "4"];
const first = true
return (
<div>
<ul>
{list.map(item => (
<li>{item}</li>
))}
</ul>
</div>
);
};
render(<App />, document.getElementById("root"));
This should work. If first is true it will only show the first element else it will show all.
const App = () => {
const list = ["1", "2", "3", "4"];
const first = false;
return (
<div>
<ul>
{first ? (
<li>{list[0]}</li>
) : (
list.map((item, index) => {
return <li>{item}</li>;
})
)}
</ul>
</div>
);
};
stackblitz
{list.map((item, i) => {
if (i === 0 && item) return <li>{item}</li>;
})}
<ul>
{
list.map((item,index) => (
(index === 0 && first) ? <li>{item}</li> : null
))
}
</ul>
Try the above code.
{list.map((item, index) => (
<li>{first ? (index === 0 ? item : null) : item}</li>
))}
The handleSortByChange function gives me an error on the browser stating that “Too many re-renders. React limits the number of renders to prevent an infinite loop.” However, on the terminal, it indicates that compliled successfully
import React, {useState} from 'react';
import './SearchBar.css';
const sortByOptions = {
'Best Match': 'best_match',
'Highest Rated': 'rating',
'Most Reviewed': 'review_count'
}
function SearchBar() {
const [ sortBy, setSortBy ] = useState('best_match')
const getSortByClass = (sortByOption) => {
if(sortBy === sortByOption) {
return 'active';
} else {
return '';
}
}
const handleSortByChange = (sortByOption) => {
setSortBy(sortByOption);
}
const renderSortByOptions = () => {
return Object.keys(sortByOptions).map((sortByOption) => {
let sortByOptionValue = sortByOptions[sortByOption];
return <li onClick={handleSortByChange(sortByOptionValue)} className={getSortByClass(sortByOptionValue)} key={sortByOptionValue}>{sortByOption}</li>
})
}
return(
<div className="SearchBar">
<div className="SearchBar-sort-options">
<ul>
{renderSortByOptions()}
</ul>
</div>
<div className="SearchBar-fields">
<input placeholder="Search Businesses" />
<input placeholder="Where?" />
</div>
<div className="SearchBar-submit">
{/* Let's Go */}
{/* <button>Let's Go</button> */}
</div>
</div>
)
}
export default SearchBar;
On your <li> tag you are directly calling the method like,
onClick={handleSortByChange(sortByOptionValue)}, which will set the state and rerender will be triggered, the same thing will happen again in this render cycle too, and thus the infinite loop.
instead do following:
<li
onClick={()=>handleSortByChange(sortByOptionValue)}
className={getSortByClass(sortByOptionValue)}
key={sortByOptionValue}
>
in this way, the handleSortByChange(sortByOptionValue) will only get executed when the <li> element is clicked.
import React, { useState } from "react";
// import './SearchBar.css';
const sortByOptions = {
"Best Match": "best_match",
"Highest Rated": "rating",
"Most Reviewed": "review_count"
};
function SearchBar() {
const [sortBy, setSortBy] = useState("best_match");
const getSortByClass = sortByOption => {
if (sortBy === sortByOption) {
return "active";
} else {
return "";
}
};
const handleSortByChange = sortByOption => {
setSortBy(sortByOption);
};
const renderSortByOptions = () => {
return Object.keys(sortByOptions).map(sortByOption => {
let sortByOptionValue = sortByOptions[sortByOption];
return (
<li
onClick={()=>handleSortByChange(sortByOptionValue)}
className={getSortByClass(sortByOptionValue)}
key={sortByOptionValue}
>
{sortByOption}
</li>
);
});
};
return (
<div className="SearchBar">
<div className="SearchBar-sort-options">
<ul>{renderSortByOptions()}</ul>
</div>
<div className="SearchBar-fields">
<input placeholder="Search Businesses" />
<input placeholder="Where?" />
</div>
<div className="SearchBar-submit">
</div>
</div>
);
}
export default SearchBar;
Working example: Stackblitz
try this on the onClick function instead:
{() => onClick={handleSortByChange(sortByOptionValue)}
i want to change the text value in a div by clicking on a ul li item.
const text = {
value1 : 'blabla1',
value2 : 'blabla2',
value3 : 'blabla3'
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div> </div>
By clicking on li 1 the div get the value1, by clicking on li 2 the div get the value2 etc.
Can someone give me some advice, thank you
i am write code
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const text = [
{ id: 1, value: "blabla1" },
{ id: 2, value: "blabla2" },
{ id: 3, value: "blabla3" }
];
const [textSelected, setTextSelected] = useState("");
const selectLi = (id) => {
let index = text.findIndex((x) => x.id === id);
if (index === -1) return;
setTextSelected(text[index].value);
};
return (
<>
<ul>
{text.map((x) => (
<li
onClick={() => {
selectLi(x.id);
}}
>
{x.id}
</li>
))}
</ul>
<br />
<div>{textSelected}</div>
</>
);
}
Work Demo
Here is another approach:
import React, { useState } from "react";
export default function App() {
const [listValues, setListValues] = useState([1, 2, 3]);
const text = { value1: "blabla1", value2: "blabla2", value3: "blabla3" };
const showTextValue = (index) => {
const newValues = [...listValues];
newValues[index] = Object.values(text)[index];
setListValues(newValues);
};
const content = listValues.map((val, index) => (
<li key={index} onClick={() => showTextValue(index)}>
{val}
</li>
));
return (
<div className="App">
<ul>{content}</ul>
</div>
);
}
import React from "react";
import "./styles.css";
export default function App() {
const clickHandler = (e) => {
alert("value is " + e.target.id);
let item = document.getElementById(e.target.id);
console.log(item.getAttribute("name"));
};
return (
<div className="App">
<ul>
<li id="blabla1" name="el1" onClick={clickHandler}>
1
</li>
<li id="blabla2" name="el2" onClick={clickHandler}>
2
</li>
<li id="blabla3" name="el3" onClick={clickHandler}>
3
</li>
</ul>
</div>
);
}
CodeSandbox example. You can also assign a value to a variable by using useState.