I have this piece of code:
let columnChooser = [productName]
function replacer (key: any) {
if (key == 1) {
message.info(`Click on item ${key}`);
columnChooser.push(productPrice)
}
}
what I want basically is to add productPrice into the existing array. With this method in my react return Ido not get a new column productPrice, I constantly have only productName. the function is from the drop-down menu and it grabs key values so an appropriate column can be added to the array.
Can anyone provide me with some insight into why this is not working
after function is run via button press I expected that new value be added into array and shown in front end
I couldn't quite understand your end goal, but from what I understand you want to update the value in a component. For that you can use the useState of React Hooks and the click event of the button.
example: https://codesandbox.io/s/6vryxv96pz
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const App = () => {
const [value, setValue] = useState("");
const [list, setList] = useState(["a", "b", "c"]);
const onChangeValue = (event) => {
setValue(event.target.value);
};
const onAddItem = () => {
setList((prevState) => {
const list = [...prevState, value];
return list;
});
setValue("");
};
return (
<div>
<ul>
{list.map((item, i) => (
<li key={i}>
<div>{item}</div>
</li>
))}
</ul>
<input type="text" value={value} onChange={onChangeValue} />
<button type="button" onClick={onAddItem} disabled={!value}>
Add
</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
in this case you need to use useState. Then your code would look like this:
// let columnChooser = [productName]
const [columnChooser, setColumnChooser] = useState ([productName]);
function replacer (key: any) {
if (key == 1) {
message.info(`Click on item ${key}`);
setColumnChooser(prevState => [...prevState, productPrice]);
// columnChooser.push(productPrice)
}
}
Related
I'm currently learning hook, and I'm writing a todo-list:
import './App.css';
import React, {useState} from 'react';
import Item from './components/Item';
function App() {
const [tasks, setTasks] = useState([]);
const addTask = (e) => {
if(e.key === 'Enter'){
let newTask = {content: e.target.value, completed: false};
console.log(newTask);
setTasks(prevTask => {
return ([...prevTask, newTask]);
});
console.log(tasks);
}
}
const completeTask = (e) =>{
let newTask = tasks.slice();
newTask[e].completed = !tasks[e].completed;
setTasks(newTask);
}
const deleteTask = (e) => {
let newTask = tasks.slice();
newTask.splice(e);
setTasks(newTask);
}
return (
<>
<header className="todo-app__header">
<h1 className="todo-app__title">todos</h1>
</header>
<section className="todo-app__main">
<input className="todo-app__input" placeholder="What needs to be done?" onKeyDown={addTask}/>
<ul className="todo-app__list" id="todo-list">
{tasks.map(item => <Item num = {tasks.indexOf(item)} text={item.content} completed = {item.completed}
onClick = {completeTask(tasks.indexOf(item))} delete = {deleteTask(tasks.indexOf(item))}/>)}
</ul>
</section>
</>
);
}
export default App;
However, adding tasks is not working!!
The newTask printed is well, but it doesn't push into the tasks array.
The tasks is still empty.
What's the problem?
Also, another problem: is it related to useeffect? I don't know what useeffect is used for.
The issue is that every time your component renders, it executes both completeTask and deleteTask, because you are using function calls as props. You need to be passing in a function object or expression. Instead of telling the component to execute completeTask on click, the function call just executes it right there as soon as the component is rendered.
The problem is with this part of your code:
<ul className="todo-app__list" id="todo-list">
{tasks.map(item => <Item num = {tasks.indexOf(item)} text={item.content} completed = {item.completed}
onClick = {completeTask(tasks.indexOf(item))} delete = {deleteTask(tasks.indexOf(item))}/>)}
</ul>
The following lines:
delete = {deleteTask(tasks.indexOf(item))}
onClick = {completeTask(tasks.indexOf(item))}
Should be changed to:
delete = {() => deleteTask(tasks.indexOf(item))}
onClick = {() => completeTask(tasks.indexOf(item))}
In normal HTML, it would look like delete="deleteFunction()", but in React, it should be delete={deleteFunction}, because writing a function with parenthesis after it is a function call, not a function expression. If you need to pass in an argument, you can either pass the argument in as a prop on the component, or change the line to delete={() => deleteFunction(arg)}, as the parenthesis and arrow makes it a function expression.
See Handling Events: https://reactjs.org/docs/handling-events.html
Your code works well and there is no problem.
React setState action in async. Try to log it in useEffect.
const App = () = => {
const [tasks, setTasks] = useState([]);
useEffect(() => {
console.log(tasks)
}, [tasks]);
const addTask = () => {
const newTask = {...};
setTasks([...tasks, newTask]);
}
}
useEffect Docs
Here as I can see, you are doing console.log(tasks) in the same function.
Try the console.log(tasks) outside function and you will see tasks array with the values you entered.
there is no problem
import React, {useState} from 'react';
function Add() {
const [tasks, setTasks] = useState([]);
const addTask = (e) => {
if(e.key === 'Enter'){
let newTask = {content: e.target.value, completed: false};
console.log(newTask);
setTasks(prevTask => {
return ([...prevTask, newTask])
});
console.log(tasks);
}
}
const completeTask = (e) =>{
let newTask = tasks.slice();
newTask[e].completed = !tasks[e].completed;
setTasks(newTask);
}
const deleteTask = (e) => {
let newTask = tasks.slice();
newTask.splice(e);
setTasks(newTask);
}
return (
<>
<header className="todo-app__header">
<h1 className="todo-app__title">todos</h1>
</header>
<section className="todo-app__main">
<input className="todo-app__input" placeholder="What needs to be done?" onKeyDown={addTask}/>
<ul className="todo-app__list" id="todo-list">
{tasks.map((item)=> <li>{item.content}</li>)}
</ul>
</section>
</>
);
}
export default Add;
it works very well and that means your Item component is your problem.
I have made a basic application to practice React, but am confused as to why, when I try to delete a single component from an state array, all items after it get deleted too. Here is my basic code:
App.js:
import React from 'react'
import Parent from './Parent';
import './App.css';
function App() {
return (
<div className="App">
<Parent />
</div>
);
}
export default App;
Parent.js:
import React, { useState } from 'react';
import ListItem from './ListItem';
import './App.css';
function Parent() {
const [itemList, setItemList] = useState([])
const [numbers, setNumbers] = useState([])
const addItem = () => {
const id = Math.ceil(Math.random()*10000)
const newItem = <ListItem
id={id}
name={'Item-' + id}
deleteItem={deleteItem}
/>
const list = [...itemList, newItem]
setItemList(list)
};
const deleteItem = (id) => {
let newItemList = itemList;
newItemList = newItemList.filter(item => {
return item.id !== id
})
setItemList(newItemList);
}
const addNumber = () => {
const newNumbers = [...numbers, numbers.length + 1]
setNumbers(newNumbers)
}
const deleteNum = (e) => {
let newNumbers = numbers
newNumbers = newNumbers.filter(n => n !== +e.target.innerHTML)
setNumbers(newNumbers);
}
return (
<div className="Parent">
List of items:
<div>
{itemList}
</div>
<button onClick={addItem}>
Add item
</button>
<div>
List of numbers:
<div>
{numbers.map(num => (
<div onClick={deleteNum}>{num}</div>
))}
</div>
</div>
<button onClick={addNumber}>
Add number
</button>
</div>
);
};
export default Parent;
ListItem.js:
import React from 'react';
import './App.css';
function ListItem(props) {
const { id, name, deleteItem } = props;
const handleDeleteItem = () => {
deleteItem(id);
}
return (
<div className="ListItem" onClick={handleDeleteItem}>
<div>{name}</div>
</div>
);
};
export default ListItem;
When I add an item by clicking the button, the Parent state updates correctly.
When I click on the item (to delete it), it deletes itself but also every item in the array that appears after it <-- UNWANTED BEHAVOUR. I only want to delete the specific item.
I have tested it with numbers too (not creating a separate component). These delete correctly - only the individual number I click on is deleted.
As far as I can tell, the individual item components are saving a reference as to what the Parent state value was when they are created. This seems like very strange behaviour to me...
How do I delete only an individual item from the itemList state array when they are made up of separate components?
Thanks
EDIT: As per the instruction from Bergi, I fixed the issue by converting the 'itemList' state value to an array of objects to render (and rerender) when the list is changed instead:
const addItem = () => {
const id = Math.ceil(Math.random()*10000);
const newItem = {
id: id,
name: 'Item-' + id,
}
const newList = [...itemList, newItem]
setItemList(newList)
}
...
React.useEffect(() => {
}, [itemList]);
...
<div className="Parent">
List of items:
<div>
{itemList.map(item => {
return (<ListItem
id={item.id}
name={item.name}
deleteItem={deleteItem}
/>);
})}
...
The problem is that your deleteItem function is a closure over the old itemList, back from the moment in which the item was created. Two solutions:
use the callback form of setItemList
don't store react elements in that list, but just plain objects (which you can use as props) and pass the (most recent) deleteItem function only when rendering the ListItems
I have react-App with redux using react-hooks functional components, it renders the list of tutors in child component correctly. i have added a search box in it and handlchange function to update the list component(child compenent). the handlechange function works fine and assign the newlist to filtered variable in side the handlechange function body however the value of filtered outside the handlechange function remains unchanged.so the updated filtered variable not assigned to TutorList Component.
the Tutors.jsx Component is as follows:
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from 'react-redux';
import { Link, useParams, useHistory } from 'react-router-dom';
import TutorList from "./TutorList";
import * as actions from "../_actions/tutorActions";
import { TextField, Button, FormControl, } from "#material-ui/core";
const initialFieldValues = {
search: ""
}
const Tutors = (props) => {
const [values, setValues] = useState(initialFieldValues)
let history = useHistory()
const dispatch = useDispatch();
// getting of tutorlist
let tutor = useSelector(state => state.tutor.list);
// sorting of tutors on date
let tutorList = tutor.sort((a, b) => b.updatedOn.localeCompare(a.updatedOn));
useEffect(() => {
dispatch(actions.fetchAll())
}, [])
console.log("tutorList:", tutorList)
// Variable to hold the filtered list before putting into state
let newList = [];
let filtered = tutorList;
//when filter changes from '' to something filtered should be updated with newlist
function handleChange(e) {
const { name, value } = e.target
const fieldValue = { [name]: value }
setValues({
...values,
...fieldValue
})
// If the search bar isn't empty
if (values.search !== "") {
// Use .filter() to determine which items should be displayed
// based on the search terms
newList = tutorList.filter(item => {
// change current item to lowercase
const lc = item.fullName.toLowerCase();
// change search term to lowercase
const filter = e.target.value.toLowerCase();
console.log("filter", filter);
// check to see if the current list item includes the search term
// If it does, it will be added to newList. Using lowercase eliminates
// issues with capitalization in search terms and search content
return lc.includes(filter);
});
} else {
newList = tutorList;
}
console.log("newList:", newList)//shows correct list
filtered = newList
console.log("filtered:", filtered)//shows correct value
}
return (
<div>
<br />
<TextField
name="search"
variant="outlined"
label="Search Tutor"
paceholder="search tutor..."
value={values.search}
onChange={handleChange}
/>
<TutorList
tutorList={filtered}
/>
<Button onClick={() => history.goBack()}
size="small" variant="contained" color="secondary">
back
</Button>
</div>
);
}
export default Tutors;
the TutorList Component should Show the newList based on filter entered in search field. any one help to workaround or better solution .thanks in advance.
There's no need to update newList, filtered from handleChange. React doesn't work this way. You won't get a rerender. The only thing you should do in your handleChange is setValue
const [textInputValue, setTextInputValue] = useState('');
function handleChange(e) {
setTextInputValue(e.target.value);
}
then do all the sorting based on
let tutor = useSelector(state => state.tutor.list)
// and textInputValue
Also there's no need to store filtered values in state.
create a new state for
const Tutors =(props)=> {
const [values, setValues] = useState(initialFieldValues)
const [filtered, setFiltered] = useState(null)
inside of handleChange() function set the filtered with that of filtered element:
function handleChange(e){
//........
console.log("newList:",newList)//shows correct list
//filtered=newList <= instead of this
setFiltered(newList) // <= do this
console.log("filtered:",filtered)//shows correct value
}
The final code should look like this:
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link, useParams, useHistory } from "react-router-dom";
import TutorList from "./TutorList";
import * as actions from "../_actions/tutorActions";
import { TextField, Button, FormControl } from "#material-ui/core";
const initialFieldValues = {
search: "",
};
const Tutors = (props) => {
const [values, setValues] = useState(initialFieldValues);
const [filtered, setFiltered] = useState(null);
let history = useHistory();
const dispatch = useDispatch();
// getting of tutorlist
let tutor = useSelector((state) => state.tutor.list);
// sorting of tutors on date
let tutorList = tutor.sort((a, b) => b.updatedOn.localeCompare(a.updatedOn));
useEffect(() => {
dispatch(actions.fetchAll());
}, []);
console.log("tutorList:", tutorList);
// Variable to hold the filtered list before putting into state
let newList = [];
let filtered = tutorList;
//when filter changes from '' to something filtered should be updated with newlist
function handleChange(e) {
const { name, value } = e.target;
const fieldValue = { [name]: value };
setValues({
...values,
...fieldValue,
});
// If the search bar isn't empty
if (values.search !== "") {
// Use .filter() to determine which items should be displayed
// based on the search terms
newList = tutorList.filter((item) => {
// change current item to lowercase
const lc = item.fullName.toLowerCase();
// change search term to lowercase
const filter = e.target.value.toLowerCase();
console.log("filter", filter);
// check to see if the current list item includes the search term
// If it does, it will be added to newList. Using lowercase eliminates
// issues with capitalization in search terms and search content
return lc.includes(filter);
});
} else {
newList = tutorList;
}
console.log("newList:", newList); //shows correct list
setFiltered(newList); // update filtered state
console.log("filtered:", filtered); //shows correct value
}
return (
<div>
<br />
<TextField
name="search"
variant="outlined"
label="Search Tutor"
paceholder="search tutor..."
value={values.search}
onChange={handleChange}
/>
{filtered && <TutorList tutorList={filtered} />}
<Button
onClick={() => history.goBack()}
size="small"
variant="contained"
color="secondary"
>
back
</Button>
</div>
);
};
export default Tutors;
Datalist is an array I'm trying to concat the boards array with the Datalist array, but when I console it doesn't reflect. On the other hand when I assign Datalist.concat(boards) to a variable it reflects example
const newArr = Datalist.concat(boards);
console.log(newArr)
(main code) please help me review it. Thanks in advance
import React, { useState, useEffect } from 'react';
import Modal from './Modal';
import { Datalist } from '../Data/Boards';
function Boards() {
const [boards, setboards] = useState(JSON.parse(localStorage.getItem('boards')) || []);
const [title, settitle] = useState('');
localStorage.setItem('boards', JSON.stringify(boards));
Datalist.concat(boards);
console.log(Datalist);
const handleChange = (e) => {
settitle(e.target.value);
};
const handleSubmit = () => {
if (title.length === 0) {
return;
}
setboards((prev) => [...prev, title]);
};
return (
<div>
<ul id="boards">
<BoardList boards={boards} />
</ul>
<Modal title={title} handleChange={handleChange} handleSubmit={handleSubmit} />
</div>
);
}
function BoardList({ boards }) {
const history = useHistory();
return (
<>
{boards.map((board, index) => (
<li
key={index}
onClick={() => {
history.push('./workspace');
}}
>
<h3>{board}</h3>
</li>
))}
</>
);
}
export default Boards;
That is the expected behaviour. The concat function does not alter the original arrays. You can read about it in the MDN docs
For your case you should be able to do Datalist = Datalist.concat(boards); and it should work like you're expecting
I am making a To Do App using useState react hook.
I have complete with Create Read and Delete parts but
I have not been able to update the state.
Can somebody please help me.
I have complete the same with Class component.
/****************************** MY app.js file ********************************************/
import React, { useState } from "react";
import "./App.css";
import ToDoList from "./Components/ToDoList";
function App() {
const [change, handleChange] = useState("");
const [items, addItem] = useState([]);
let handleSubmit = (e) => {
e.preventDefault();
// console.log(change)
if (change !== "") {
addItem([...items, { text: change, key: Date.now() }]);
handleChange("");
}
};
let removeTask = (key) => {
let item = items.filter((ele) => {
return ele.key !== key;
});
console.log(item);
addItem([...item]);
};
let updateToDo = (value, key) => { // <<<<<<< I need to make changes in this piece of code.
let allItem = items.map((e) => {
if (e.key === key) {
e.text = value;
}
console.log(...allItem);
// addItem([...items, { allItem }]);
});
};
return (
<div className="toDoContainer">
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => handleChange(e.target.value)}
value={change}
placeholder="Add Item"
/>
<button>Add Item</button>
</form>
<ToDoList items={items} removeTask={removeTask} updateToDo={updateToDo} />
</div>
);
}
export default App;
/*************************************** My ToDoList.js *************************************/
import React from "react";
import "./ToDoList.css";
function ToDoList({ items, removeTask, updateToDo }) {
let toDoItems = items.map((item) => {
return (
<div className="toDoItems" key={item.key}>
<p>
<input
type="text"
id = {item.key}
value={item.text}
onChange={(e) => updateToDo(e.target.value, item.key)}
/>
<span onClick={() => removeTask(item.key)}>✘</span>
</p>
</div>
);
});
return <div>{toDoItems}</div>;
}
export default ToDoList;
You can map items into new array and when the item key matches the key parameter update the text property.
let updateToDo = (value, key) => {
const allItem = items.map(item => {
const newItem = {...item};
if (item.key === key) {
newItem.text = value;
}
return newItem;
});
console.log(...allItem);
addItem(allItem);
};
buddy!
First fo all, I suggest you can read document about React Hooks, it have clear explain how to update State when you using useState, I split several parts below:
On here const [items, addItem] = useState([]);, The Hooks useState will return a array, the first item is your value of state, at this time is a empty array [], the second item is a method which can update value of state.
Next, in your update method updateToDo, you used map to update original value of state and create the new value of state. so why didn't you call addItem to update your value of state?(Maybe you tried, but I have no idea for why you comment out that line?)
You just need to pass new value of state for addItem, and I suggest you can rename it to setItem instead of addItem.
You can following:
let updateToDo = (value, key) => { // <<<<<<< I need to make changes in this piece of code.
let allItem = items.map((e) => {
if (e.key === key) {
e.text = value;
}
addItem(allItem);
});
};