I am currently working on a react.js to do list. The code below is what i've done so far which are relevant to the question
Main File (App.js)
import './App.css';
import Navbar from './components/Header'
import Container from './components/Container.js';
import Input from './components/Input';
import { useState } from 'react';
function App() {
let todo =[
{id:Math.random()*10000,
title: "Walk the dog",
completed:false},
{id:Math.random()*10000,
title: "Wash Dishes",
completed:false},
]
const [list,setList] = useState(todo)
function addTodo(e) {
let value = e.target.previousElementSibling.value;
let newtodo = [...todo,{id: Math.random()*10000, title: value, completed:false}]
setList(newtodo)
console.log(todo)
}
return (
<div className="body">
<Navbar className="Navbar"/>
<Input method = {addTodo} />
<Container list={newtodo}/>
</div>
);
}
export default App;
Container Component
const Container =({list , method})=> {
return(
<div className="container">
{list.map(element => {
return(
<TodoComponent className="todo-list" data={element}/>
)
})}
</div>
)
}
export default Container
Todo List Component
import React from "react";
const TodoComponent = ({data})=> {
return(
<div className="todo-list" >
<h3 key={data.id} >{data.title}</h3>
<i className="fas fa-trash"></i>
</div>
)
}
export default TodoComponent
Form Input
import React from "react"
const Input = ({method}) => {
return (
<div className="form">
<input placeholder="add task.."></input>
<button onClick= {method}>
Add
</button>
</div>
)
}
export default Input
The problem is, whenever I click the button next to the input field, the "todo" array updates, but the updates do not get rendered in the to do list. How do i fix this? Thanks in advance. If this was too confusing, please let me know.
Your addTodo function is incorrect. instead ..todo it should be ...list. try this
function addTodo(e) {
let value = e.target.previousElementSibling.value;
let newtodo = [...list,{id: Math.random()*10000, title: value, completed:false}]
setList(newtodo)
console.log(todo)
}
and you are not passing list state to <Container/> component
insted of
<Container list={newtodo}/>
it should be
<Container list={list}/>
First verify if you have the desired value in your addTodo function. The problem is you are updating the todo array which doesn't change because it is not a state variable. Try:
function addTodo(e) {
let value = e.target.previousElementSibling.value;
setList(list => [...list, {id: Math.random()*10000, title: value, completed:false})
}
Related
Why the input only taking inputs from second input only?
import React, { useState } from "react";
import Item from "./Components/Item";
import "./ToDo.css";
function ToDo() {
let toDoIs = document.getElementById("toDoInput");
const [ToDo, setToDoIs] = useState("d");
const [ToDoArray, setToDoArray] = useState([]);
return (
<div>
<h1>ToDo</h1>
<input
id="toDoInput"
onChange={() => {
setToDoIs(toDoIs.value);
}}
type="text"
/>
<button
onClick={() => {
setToDoArray([...ToDoArray, { text: ToDo }]);
toDoIs.value = "";
}}
>
Add
</button>
<Item push={ToDoArray} />
</div>
);
}
export default ToDo;
Why the second input only works, which means whenever I use submit the value from second input only stored and displayed. I don't know why this happens.
There's a few problems here...
Don't use DOM methods in React. Use state to drive the way your component renders
Your text input should be a controlled component
When updating state based on the current value, make sure you use functional updates
import { useState } from "react";
import Item from "./Components/Item";
import "./ToDo.css";
function ToDo() {
// naming conventions for state typically use camel-case, not Pascal
const [toDo, setToDo] = useState("d");
const [toDoArray, setToDoArray] = useState([]);
const handleClick = () => {
// use functional update
setToDoArray((prev) => [...prev, { text: toDo }]);
// clear the `toDo` state via its setter
setToDo("");
};
return (
<div>
<h1>ToDo</h1>
{/* this is a controlled component */}
<input value={toDo} onChange={(e) => setToDo(e.target.value)} />
<button type="button" onClick={handleClick}>
Add
</button>
<Item push={toDoArray} />
</div>
);
}
export default ToDo;
I'm implementing a project where
I have a array of 44 object data
When I type a it returns 37 data immediately by onChange()
After type ad it return 20
The Problem is when I return back to a by backspace. It stay on 20.
How can I get back 37 data again.
Code of Root.jsx
import React, { Component } from 'react'
import icons from './services/icons'
import IconCard from './components/IconCard'
import Header from './components/Header'
import Search from './components/Search'
const icon = new icons()
class Root extends Component {
state = {
data: icon.getIcon(),
}
getBadge = (e) => {
console.log(e)
const searched = this.state.data.filter(
item => {
if (e === '') {
return item
} else if (item.title.toLowerCase().includes(e.toLowerCase())) {
console.log(item)
return item
}
}
)
this.setState({ data:searched })
}
render() {
const data = this.state.data
return (
<>
<>
<Header />
<Search getBadge={this.getBadge} />
</>
<div className='container'>
<IconCard data={data} />
</div>
</>
)
}
}
export default Root
state data be like
state={
data:data
}
data
{
"title": "Academia",
"hex": "41454A"
},
{
"title": "Academia",
"hex": "41454A"
}
Code of Search.jsx
import React, { Component } from 'react';
class Search extends Component {
handleChange = (e) => {
this.props.getBadge(e.target.value)
}
render() {
// console.log(this.state.search)
return (
<div className='container pb-3'>
<div className="row">
<div className="col-md-3 align-self-center ">
<input type="text" className="form-control" placeholder="Search by brand..." onChange={this.handleChange} />
</div>
</div>
</div>
)
}
}
export default Search;
I understood your problem. You are mutating the original data whenever the search text is changing. Actually, you should not do that.
Instead,
import React, { Component } from 'react'
import icons from './services/icons'
import IconCard from './components/IconCard'
import Header from './components/Header'
import Search from './components/Search'
const icon = new icons()
class Root extends Component {
state = {
data: icon.getIcon(),
searchText: '',
}
getBadge = (search) => {
console.log(search)
return this.state.data.filter(
item => {
if (item.title.toLowerCase().includes(search.toLowerCase())) {
console.log(item)
return true;
}
return false;
}
)
}
render() {
const data = this.state.data
return (
<>
<>
<Header />
<Search
value={this.state.searchText}
onChange={(value) => this.setState({searchText: value})} />
</>
<div className='container'>
<IconCard data={this.getBatchData(this.state.searchText)} />
</div>
</>
)
}
}
export default Root
Set searchText state in the component
Change the props of the <Search /> component
Update the state when the search updates
Update the getBatchData() as per above code.
Everytime you update the search text, the data will remains same, but the filter will return the results according to search text
In your function getBadge :
const searched = this.state.data.filter(...)
this.setState({ data:searched })
You are replacing the state with the object you found. So if the data object had 44 elements, after a search it will only have the filtered elements. All the other elements are gone.
You should consider filtering from a constant object instead of state.data
I'm relatively new to React and am trying to set the state of an object inside the child component(SportTile) for the onclick event. I'm comfortable updating the state of a single variable by using individual useState() hooks but, that is a cumbersome task when you have a lot of variables. So I used an object named : isClicked which stores a boolean value for various sports on whether they are selected by the user or not.
The functionality that I'm looking for is, whenever a SportTile gets clicked on, its isClicked["sportname"] value is set to true and the rest are set to false. Only one sport can be true at once.
Upon console logging the isClicked object in the onclick() function, I got the desired values but they weren't updated in the Parent component's(Booking) h1 tag
import React from 'react';
import SportTile from '../SportTile';
import './booking.css';
import { useState } from 'react';
import SportsSoccerRoundedIcon from '#mui/icons-material/SportsSoccerRounded';
import SportsBasketballRoundedIcon from '#mui/icons-material/SportsBasketballRounded';
import SportsVolleyballIcon from '#mui/icons-material/SportsVolleyball';
import SportsTennisIcon from '#mui/icons-material/SportsTennis';
const Booking = () => {
const [isClicked, setIsClicked] = useState({
Football: false,
Basketball: false,
Volleyball: false,
Tennis: false,
});
return (
<div className='booking'>
<div className='booking__body'>
<div className='booking__left'>
<h1>SELECT SPORT</h1>
<SportTile
sportname={'Football'}
icon={<SportsSoccerRoundedIcon />}
clicked={setIsClicked}
isClicked={isClicked}
// test={setTestclicked}
// istestClicked={istestClicked}
/>
<SportTile
sportname={'Basketball'}
icon={<SportsBasketballRoundedIcon />}
clicked={setIsClicked}
isClicked={isClicked}
/>
<SportTile
sportname={'Volleyball'}
icon={<SportsVolleyballIcon />}
clicked={setIsClicked}
isClicked={isClicked}
/>
<SportTile
sportname={'Tennis'}
icon={<SportsTennisIcon />}
clicked={setIsClicked}
isClicked={isClicked}
/>
<h1>Football : {isClicked.Football.toString()} </h1>
<h1>Basketball : {isClicked.Basketball.toString()} </h1>
<h1>Volleyball : {isClicked.Volleyball.toString()} </h1>
<h1>Tennis : {isClicked.Tennis.toString()} </h1>
</div>
<div className='booking__right'>Right Side</div>
</div>
</div>
);
};
export default Booking;
import { Button } from '#mui/material';
import React from 'react';
import './sportTile.css';
const SportTile = ({
sportname,
icon,
clicked,
isClicked,
}) => {
function onclick() {
Object.keys(isClicked).map((key) => {
if (key == sportname) {
isClicked[key] = true;
} else {
isClicked[key] = false;
}
});
clicked(isClicked);
console.log(isClicked);
}
return (
<Button className='sportname__button' onClick={onclick}>
<div className='sportname__buttonColumn'>
{icon}
{sportname}
</div>
</Button>
);
};
export default SportTile;
Maybe I'm missing the obvious but would appreciate anyone who could point me in the right direction
You should never pass the original setState method.
create a new method in the Booking component:
const setIsClickedWrapper = (sportKey) = {
setIsClicked((isClicked) => Object.keys(isClicked).map((key) => sportKey === key)
}
and in the SportTile component just call:
const onclick = () => { setIsClickedWrapper(sportname) }
but I think it will be better if isClicked will be just a string of the current clicked sport key and then it's enough:
const setIsClickedWrapper = (sportKey) = {
setIsClicked(sportKey)
}
Hi I have mapped some json data named "projectsData" and I am trying to "bind" an onClick event with a setState hook. The mapping works except for the "onClick" does not work when clicking the grid item. In my case I want to update filterproject value with the project.id value from that target.
Right now when I click an item it does nothing.
How do I successfully map a function to "onClick" while using functional components?
Below is the parent Component
import React, { useEffect, useState } from "react";
import projectsData from '../data/projectsData';
import Project from './Projects';
const App = (props) => {
const [projects] = useState(() => (projectsData.map((project) => <Project id={project.id} project={project} onClick={() => {setFilterProject(project.id)}}/>)));
const [filterproject, setFilterProject] = useState(null);
return (
<body>
<div id='sepLine'>
<div id="visHolder">
<div id="visContainer" style={{position: "relative", width: "840px", height: "1823px"}} >
{projects}
</div>
</div>
</div>
</body>
);
}
export default App;
And here is the Child Component - "Project"
import React, { useRef } from "react";
const Project = (props) => {
const {projectClick, project} = props;
return (
<div className={`lineDiv gridItem y${project.start}-${project.end} ${project.kind}`} style={{positon: "absolute"}} onClick={projectClick}>
<h5>{project.title}</h5>
<br></br>
<p className="year">
<span className="yearsstart">{project.start}</span> - <span className="yearsend">{project.end}</span>
<br></br>
<span className="kind">{project.kind}</span>
</p>
</div>
)
}
export default Project
below is a screen grab of Console showing one of the mapped projects and it's onClick parameters. I can see it but when I click nothing happens. Any help would be great!
You pass click handler to a prop called onClick when setting initial state
const [projects] = useState(() => projectsData.map((project) => (
<Project
id={project.id}
project={project}
onClick={() => {setFilterProject(project.id)}}
/>
));
but access it as projectClick in the component
const { projectClick, project } = props;
...
<div
className={`lineDiv gridItem y${project.start}-${project.end} ${project.kind}`}
style={{positon: "absolute"}}
onClick={projectClick}
>
...
</div>
Fix by accessing the correct prop
const { onClick, project } = props;
...
<div
className={`lineDiv gridItem y${project.start}-${project.end} ${project.kind}`}
style={{positon: "absolute"}}
onClick={onClick}
>
...
</div>
setState() doesn't work on first click ! the state value gets updated only on second , third ....clicks. i used proper contexts and imports to handle the states!
I'll quickly summarize what im doing top nav bar has two buttons , home and cart.
Side nav bar has three hero buttons, on click renders the respective hero store which has tshirts , socks and shoes with + and - buttons to increase or decrease the quantity.
on each click the value of span that displays the quantity increases correctly but the cart buttons shows the quantity excluding the first clicks. Like when i increment the tshirts value to 1 , the cart button doesn't show any value ,as i increment the tshirts value to 2 the cart button shows 1
cartButton uses the state CartValue
tshirts,socks,shoes use the state HeroGoods
(live demo) click here to see the what im talking about
i'm not sure if im allowed to post all the components and external links like github here. but anyways if you guys cant see where i went wrong from the code below , here's link to the github repo
import React , {useState,useEffect}from 'react';
import Navmenu from './Navmenu'
import SideNav from './SideNav'
import ActionDiv from './ActionDiv'
import ActionHeroStore from './ActionHeroStore'
import ActionCart from './ActionCart'
import '../css/main.css'
export const HeroContext=React.createContext()
const emptyGood={
tshirts:0,
shoes:0,
socks:0,
}
const emptyCart={
batman:{
tshirts:0,
shoes:0,
socks:0,
},
superman:{
tshirts:0,
shoes:0,
socks:0,
},
greenlantern:{
tshirts:0,
shoes:0,
socks:0,
},
}
function empty()
{
return null
}
function App() {
const [hero,setHero]=useState(null)
const [cartValue,setCartValue]=useState(emptyCart)
const [batmanGoods,setBatmanGoods]=useState(emptyGood)
const [supermanGoods,setSupermanGoods]=useState(emptyGood)
const [greenLanternGoods,setGreenLanternGoods]=useState(emptyGood)
const [showCart,setShowCart]=useState(false)
function handleUpdateGoods(hero,obj){
hero=='batman'?
setBatmanGoods(prevState=>{
return {...prevState,...obj}
}):
hero=='superman'?
setSupermanGoods(prevState=>{
return {...prevState,...obj}
}):
hero=='greenlantern'?
setGreenLanternGoods(prevState=>{
return {...prevState,...obj}
}):
empty()
}
function handleHeroSelect(name){
setHero(prevState=>prevState=name)
}
function handleCartValue(value)
{
setCartValue(value)
}
function handleShowCart(status)
{
setShowCart(status)
}
function giveHeroGoods(hero,element)
{
return (
hero=='batman'?batmanGoods[element]:
hero=='superman'?supermanGoods[element]:
hero=='greenlantern'?greenLanternGoods[element]:empty()
)
}
function handleUpdateCart(name){
name=='batman'?
setCartValue(prevState=>{
return {...prevState,batman:{...batmanGoods}}
}):
name=='superman'?
setCartValue(prevState=>{
return {...prevState,superman:{...supermanGoods}}
}):
name=='greenlantern'?
setCartValue(prevState=>{
return {...prevState,greenlantern:{...greenLanternGoods}}
}):
empty()
}
const heroContextValue={
handleHeroSelect,
handleCartValue,
handleUpdateGoods,
giveHeroGoods,
handleUpdateCart,
handleShowCart
}
return (
<>
<HeroContext.Provider value={heroContextValue}>
<Navmenu cartValue={cartValue}/>
<div className="mainContent">
<SideNav cartValue={cartValue}/>
{hero==null && !showCart &&<ActionDiv/>}
{hero!==null && !showCart && <ActionHeroStore hero={hero}/>}
{showCart && <ActionCart cartValue={cartValue}/>}
</div>
</HeroContext.Provider>
</>
)
}
export default App;
import React ,{useContext} from 'react'
import {HeroContext} from './App'
export default function Navmenu(props) {
const {cartValue}=props
const {handleHeroSelect,handleShowCart}=useContext(HeroContext)
function giveGoodsSum(obj)
{
return obj.tshirts+obj.socks+obj.shoes
}
function giveCartValue(cartValue){
let sum=0
for(let key in cartValue)
{
sum=sum+giveGoodsSum(cartValue[key])
}
return(
sum!==0?sum:null
)
}
return (
<div
className="navMenu"
>
<button
className="homeButton"
onClick={()=>{
handleHeroSelect(null)
handleShowCart(false)
}}
>
Home
</button>
<button
className="cartButton"
onClick={()=>{
handleHeroSelect(null)
handleShowCart(true)
}}
>
cart
<span
>
{giveCartValue(cartValue)}
</span>
</button>
</div>
)
}
import React ,{useContext} from 'react'
import {HeroContext} from './App'
export default function SideNav() {
const {handleHeroSelect}=useContext(HeroContext)
return (
<div className="sideNav">
<div
className="batman"
onClick={()=>handleHeroSelect('batman')}
/>
<div
className="superman"
onClick={()=>handleHeroSelect('superman')}
/>
<div
className="greenlantern"
onClick={()=>handleHeroSelect('greenlantern')}
/>
</div>
)
}
import React from 'react'
import ActionHeroStoreGoods from './ActionHeroStoreGoods'
export default function ActionHeroStore(props) {
const {hero}=props
return (
<div className={`actionHeroStore ${hero}div`}>
<h3>{hero}</h3>
<div className="actionHeroStore_goods">
<ActionHeroStoreGoods hero={hero}/>
</div>
</div>
)
}
import React, { Fragment,useContext } from 'react'
import {HeroContext} from './App'
export default function ActionHeroStoreGoods({hero}) {
const {giveHeroGoods,
handleUpdateGoods,
handleUpdateCart
}=useContext(HeroContext)
const goods=['tshirts','shoes','socks'];
const goodsElement=goods.map((element,index) => {
return <Fragment key={index}>
<div className="soloGood">
<span>{element}</span>
<button
onClick={
()=>decrement(hero,element)
}>-</button >
<span>{giveHeroGoods(hero,element)}</span>
<button onClick={
()=>{
increment(hero,element)
handleUpdateCart(hero)
}
}>+</button>
</div>
</Fragment>
})
function increment(hero,element){
let updateObj={};
updateObj[element]=giveHeroGoods(hero,element)+1
handleUpdateGoods(hero,updateObj)
}
function decrement(hero,element){
if(giveHeroGoods(hero,element)>0)
{
let updateObj={};
updateObj[element]=giveHeroGoods(hero,element)-1
handleUpdateGoods(hero,updateObj)
}
}
return (
<>
{goodsElement}
</>
)
}
The problem is not in setState. The problem in the code. handleUpdateCart() function is called before the *Goods states are changed. So It works with old data. If you will add in the your 'App.js' file the following fragment:
...
...
function giveHeroGoods(hero,element)
{
return (
hero=='batman'?batmanGoods[element]:
hero=='superman'?supermanGoods[element]:
hero=='greenlantern'?greenLanternGoods[element]:empty()
)
}
// FROM HERE
React.useEffect(() => {
handleUpdateCart('batman');
}, [
batmanGoods
]);
React.useEffect(() => {
handleUpdateCart('superman');
}, [
supermanGoods
]);
React.useEffect(() => {
handleUpdateCart('greenlantern');
}, [
greenLanternGoods
]);
// TILL HERE
function handleUpdateCart(name){
...
...