How to add a new textfield on button click and add integer value of that input field to an array in react - javascript

I want to add a new input field on button click and add the integer value of that input field to an array in react
const [price, setPrice] = useState([])
const [count, setCount] = useState([1])
const addNewTextField = () => setCount(prev => [...prev,1])
const addInputValue= () => {
setPrice()
console.log(price)
}
<Button onClick={addNewTextField}>Add TextField</Button >
{
count.map((item, i) => {
return (
<TextField key={i} value={item.value} id={i} type='text' />
)
})
}
<Button onClick={addInputValue}>submit</Button >
first input value is 100,
second input value is 200,
result should be like this when I add new input field:
[100,200]

Try like below. You can keep only price state.
import { useState } from "react";
const App = () => {
const [price, setPrice] = useState([""]);
const addNewTextField = () => setPrice((prev) => [...prev, ""]);
const addInputValue = (i, newValue) => {
console.log(i, newValue);
setPrice((prevState) =>
prevState.map((value, valueIndex) =>
valueIndex === i ? newValue : value
)
);
};
console.log(price);
return (
<>
<button onClick={addNewTextField}>Add TextField</button>;
{price.map((item, i) => {
return (
<input
key={i}
placeholder={`input ${i}`}
// value={item}
id={i}
type="text"
onChange={(e) => addInputValue(i, e.target.value)}
/>
);
})}
<button onClick={addInputValue}>submit</button>
</>
);
};
export default App;
Code sandbox

const [price, setPrice] = useState([]);
const [count, setCount] = useState([1]);
const [value, setValue] = useState("");
const addNewTextField = () => setCount(prev => [...prev,prev + 1]);
const addInputValue= () => {
setPrice(price.concat(value));
console.log(price)
}
return(
<div>
<Button onClick={addNewTextField}>Add TextField</Button >
{
count.map((item, i) => {
return (
<TextField key={i} value={value} id={i} type='text'
onChange={e => setValue(e.target.value)} />
);
})
}
<Button onClick={addInputValue}>submit</Button >
</div>
);
Button and TextField components:
function Button(props){
return(
<button onClick={props.onClick}>{props.children}</button>
);
}
function TextField(props){
return(
<input type="text" id={props.id} value={props.value}
onChange={e=>props.onChange(e)}>
);
}

Related

When editing a todo it automatically sets in to an empty string rather than its current value, how can I fix this?

When editing a todo it will automictically clear the value, I would like it to contain its original value so you can edit upon it rather than typing everything all over again.
Im assuming usestate is setting the editingText into an empty string in which case in will always output a empty value?
Also I would like to incorporate a cancel button in which cancels eiditing and returns back to its current value.
const App = () => {
const [todos, setTodos] = React.useState([]);
const [todo, setTodo] = React.useState("");
const [todoEditing, setTodoEditing] = React.useState(null);
const [editingText, setEditingText] = React.useState("");
function handleSubmit(e) {
e.preventDefault();
const newTodo = {
id: new Date().getTime(),
text: todo,
completed: false,
};
setTodos([...todos].concat(newTodo));
setTodo("");
}
function deleteTodo(id) {
let updatedTodos = [...todos].filter((todo) => todo.id !== id);
setTodos(updatedTodos);
}
function toggleComplete(id) {
let updatedTodos = [...todos].map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
setTodos(updatedTodos);
}
function submitEdits(id) {
const updatedTodos = [...todos].map((todo) => {
if (todo.id === id) {
todo.text = editingText;
}
return todo;
});
setTodos(updatedTodos);
setTodoEditing(null);
}
return (
<div id="todo-list">
<h1>Todo List</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setTodo(e.target.value)}
value={todo}
/>
<button type="submit">Add Todo</button>
</form>
{todos.map((todo) => (
<div key={todo.id} className="todo">
<div className="todo-text">
{todo.id === todoEditing ? (
<input
type="text"
onChange={(e) => setEditingText(e.target.value)}
/>
) : (
<div>{todo.text}</div>
)}
</div>
<div className="todo-actions">
{todo.id === todoEditing ? (
<button onClick={() => submitEdits(todo.id)}>Submit Edits</button>
) : (
<button onClick={() => setTodoEditing(todo.id)}>Edit</button>
)}
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</div>
</div>
))}
</div>
);
};
export default App;
Use defaultValue to set the initial value of the input
<div className="todo-text">
{todo.id === todoEditing ? (
<input
defaultValue={todo.text}
type="text"
onChange={(e) => setEditingText(e.target.value)}
/>
) : (
<div>{todo.text}</div>
)}
</div>
Adding a cancel button is just setting your edit id to null
<>
<button onClick={() => submitEdits(todo.id)}>
Submit Edits
</button>
<button onClick={() => setTodoEditing(null)}>Cancel</button>
</>
Stackblitz: https://stackblitz.com/edit/react-ts-rarpqn?file=App.tsx
Use value prop
{todo.id === todoEditing ? (
<input
value={todo.text}
type="text"
onChange={(e) => setEditingText(e.target.value)}
/>
) : (
<div>{todo.text}</div>
)}

TODOLIST Update in REACT

So I was trying to update the value I got by the Addlist and I tried this but this isn;t working. Also when I click on the '+' button without writing anything, an empty list is created. How should I stop it. I've attached a code below.
import React from "react";
import "./App.css";
import { useState } from "react";
import TodoList from "./components/TodoList";
function App() {
const [input, setInput] = useState("");
const [list, setList] = useState([]);
const updateList = (e) => {
setInput(e.target.value);
};
const AddList = () => {
console.log("value added")
setList((addValue) => {
return [...addValue, input];
});
setInput("");
};
const updateItems=(id)=>{
const newValue=[...list].map((newVal)=>{
if(input.id===id){
input.text='';
}
return newVal;
})
setList(newValue);
}
const deleteItems = (id) => {
console.log("deleted");
setList((addValue) => {
return addValue.filter((element, index) => {
return index !== id;
});
});
};
return (
<div className="todo-app">
<h1> Enter Anything</h1>
<input
type="text"
placeholder="Add anything"
value={input}
onChange={updateList}
/>
<button onClick={AddList}>+</button>
<ul>
{list.map((itemsvalue, id) => {
return (
<TodoList
itemsValue={itemsvalue}
key={id}
onSelect={deleteItems}
id={id}
onUpdate={updateItems}
/>
);
})}
</ul>
</div>
);
}
export default App;
Any kind of help would be appreciated. Also if I want to split this into multiple components is there a way to do.
When user clicks on the add button there is the check for empty String AddList method
for ex:- User updates second index value, second position value will get updated.
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const [index, setIndex] = useState(null);
const updateList = (e) => {
setInput(e.target.value);
};
useEffect(() => {
setList(list);
console.log(list, '<>?');
}, [index]);
const AddList = () => {
if (input.trim() !== '') {
setList([...list, input]);
}
setInput('');
};
const updateValue = (index) => {
console.log(list[index]);
setIndex(index);
if (list[index].trim() !== '') {
setInput(list[index]);
}
};
const UpdateList = () => {
list[index] = input;
console.log(list, 'before <>?');
setIndex(null);
setInput('');
};
return (
<div>
<input type="text" placeholder="Add anything" value={input} onChange={updateList} />
<button disabled={!index && !list.length === 0} onClick={AddList}>
Add
</button>
<button disabled={input.trim() === ''} onClick={UpdateList}>
Update
</button>
{list.map((m, index) => (
<h1 style={{ border: '1px solid black' }} onClick={() => updateValue(index)}>
{m}
</h1>
))}
</div>
);

Multiple checkbox filtering in React

I'm struggling to create multiple Checkbox filtering in React with Material-UI. The difficult is that checkbox options are created dynamically from received data and should be split by each type of Select components
But I can't properly create states to manage them in the App.
Any ideas how to do it correctly?
Options Component
function filterDuplicate(arr) {
return arr.filter((elem, index, array) => array.indexOf(elem) === index);
}
export default function Options({ stat }) {
const [form, setForm] = useState({
publicationType: "",
termType: "",
reportGroup: "",
reportState: "",
reportFormat: ""
});
const publicationTypes = filterDuplicate(
stat.map((data) => data.publicationType)
);
const termTypes = filterDuplicate(stat.map((data) => data.termType));
const reportGroups = filterDuplicate(stat.map((data) => data.reportGroup));
const reportStates = filterDuplicate(stat.map((data) => data.reportState));
const reportFormats = filterDuplicate(stat.map((data) => data.reportFormat));
function handleSubmit(e) {
e.preventDefault();
console.log(form);
}
return (
<>
<form onSubmit={handleSubmit} className="options">
<Select type="Publication type" options={publicationTypes} />
<Select type="Term type" options={termTypes} />
<Select type="Report group" options={reportGroups} />
<Select type="Status" options={reportStates} />
<Select type="File Type" options={reportFormats} />
<Button variant="contained" color="secondary" type="submit">
RESET
</Button>
</form>
</>
);
}
Options.propTypes = {
stat: PropTypes.arrayOf(PropTypes.shape({})).isRequired
};
Select Component
export default function Select({ type, options }) {
const [check, setCheck] = useState([]);
const [value, setValue] = useState("");
const classes = useStyles();
const handleChange = (e) => {
if (e.target.checked) {
setCheck([...check, e.target.value]);
} else {
setCheck(check.filter((id) => id !== e.target.value));
}
const str = check.join(", ");
setValue(str);
};
return (
<>
<FormControl className={classes.formControl}>
<InputLabel id="select-label" className={classes.label}>
{type}
</InputLabel>
<MaterialSelect labelId="select-label" id="input-select">
{options.map((option) => (
<Checkbox option={option} key={option} onChange={handleChange} />
))}
</MaterialSelect>
</FormControl>
</>
);
}
Checkbox Component
const Checkbox = React.forwardRef(({ option, onChange }, ref) => {
return (
<div ref={ref}>
<FormControlLabel
control={<MaterialCheckbox onChange={onChange} color="primary" />}
label={option}
value={option}
/>
</div>
);
});
Checkbox.propTypes = {
option: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
};
export default Checkbox;
https://codesandbox.io/s/checkbox-filter-vqex7?file=/src/Options.js:0-1593
The problem is you only pass data from parent to child. On App.js they are two separate components. You should write a function on App.js that connects these two components.
Your App.js
export default function App() {
const [stat, setState] = useState([]);
useEffect(() => {
setState(data);
}, []);
return (
<div className="App">
<div>
<div>
<h1>Information</h1>
</div>
<Options stat={stat} />
<Data info={stat} />
</div>
</div>
);
}
App.js when filtering method added
export default function App() {
const [stat, setState] = useState([]);
useEffect(() => {
setState(data);
}, []);
const handleFilter = (selectedValue) => {
console.log(selectedValue);
//apply filtering here and setState again
}
return (
<div className="App">
<div>
<div>
<h1>Information</h1>
</div>
<Options stat={stat} handleFilter={handleFilter} />
<Data info={stat} />
</div>
</div>
);
}
Options.js
export default function Options({ stat, handleFilter }) {
const [form, setForm] = useState({
publicationType: "",
termType: "",
reportGroup: "",
reportState: "",
reportFormat: ""
});
const publicationTypes = filterDuplicate(
stat.map((data) => data.publicationType)
);
const termTypes = filterDuplicate(stat.map((data) => data.termType));
const reportGroups = filterDuplicate(stat.map((data) => data.reportGroup));
const reportStates = filterDuplicate(stat.map((data) => data.reportState));
const reportFormats = filterDuplicate(stat.map((data) => data.reportFormat));
function handleSubmit(e) {
e.preventDefault();
console.log(form);
}
return (
<>
<form onSubmit={handleSubmit} className="options">
<Select
type="Publication type"
options={publicationTypes}
handleFilter={handleFilter}
/>
<Select
type="Term type"
options={termTypes}
handleFilter={handleFilter}
/>
<Select
type="Report group"
options={reportGroups}
handleFilter={handleFilter}
/>
<Select
type="Status"
options={reportStates}
handleFilter={handleFilter}
/>
<Select
type="File Type"
options={reportFormats}
handleFilter={handleFilter}
/>
<Button variant="contained" color="secondary" type="submit">
RESET
</Button>
</form>
</>
);
}
Select.js
export default function Select({ type, options, handleFilter }) {
const [check, setCheck] = useState([]);
const [value, setValue] = useState("");
const classes = useStyles();
const handleChange = (e) => {
if (e.target.checked) {
handleFilter(e.target.value);
setCheck([...check, e.target.value]);
} else {
setCheck(check.filter((id) => id !== e.target.value));
}
const str = check.join(", ");
setValue(str);
};
return (
<>
<FormControl className={classes.formControl}>
<InputLabel id="select-label" className={classes.label}>
{type}
</InputLabel>
<MaterialSelect labelId="select-label" id="input-select">
{options.map((option) => (
<Checkbox option={option} key={option} onChange={handleChange} />
))}
</MaterialSelect>
</FormControl>
</>
);
}
With that structure, you can receive checked value on App.js and filter data.

prevent useEffect to fire on initial render

I want to do a debounce for custom input, but my problem is I can't stop useEffect from trigger on initial render
import { useDebouncedCallback } from "use-debounce";
interface myInputProps {
getValue: any;
}
const MyInput = ({ getValue }: myInputProps) => {
const [value, setValue] = useState("");
React.useEffect(() => {
getValue(value);
}, [value]);
return (
<input type="text" value={value} onChange={e => setValue(e.target.value)} />
);
};
export default function App() {
const [debouncedCallback] = useDebouncedCallback(value => {
console.log(value);
}, 1000);
return (
<div className="App">
<MyInput getValue={debouncedCallback} />
</div>
);
}
https://codesandbox.io/s/upbeat-lamport-ukq70?file=/src/App.tsx
I've also tried useLayoutEffect but it doesn't solve the problem.
We could use useRef to keep track of if it's the first time the useEffect hook is being run.
https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables
Sandbox link: https://codesandbox.io/s/confident-cerf-flkf2?file=/src/App.tsx
const MyInput = ({ getValue }: myInputProps) => {
const [value, setValue] = useState("");
const first = useRef(true);
React.useEffect(() => {
if (first.current) {
first.current = false;
return;
}
getValue(value);
}, [value]);
return (
<input type="text" value={value} onChange={e => setValue(e.target.value)} />
);
};
Set initial value to undefined and you can explicitly check for undefined. Once the user enter, it won't be undefined.
const MyInput = ({ getValue }: myInputProps) => {
const [value, setValue] = useState(undefined);
React.useEffect(() => {
if (value === undefined) {
return;
}
getValue(value);
}, [value]);
return (
<input type="text" value={value} onChange={e => setValue(e.target.value)} />
);
};

How to add a whole array to useState variable

I'm trying to add a whole array to useState variable
import React, { Fragment, useState, useEffect } from 'react';
import { Form, Button, Popover, OverlayTrigger } from 'react-bootstrap';
const Filter = props => {
const [formData, setFormData] = useState({
filter: ''
});
const [items, setItems] = useState([]);
const [retrievedItems, setRetrievedItems] = useState([]);
const addToFilter = newFilter => {
let retrievedFilter = ["da vinci","paris", "london"];
console.log(retrievedFilter);
if (retrievedFilter.length > 0) {
setRetrievedItems([...retrievedItems, retrievedFilter]);
retrievedFilter = 0;
setRetrievedItems([...retrievedItems, newFilter]);
} else {
setItems([...items, newFilter]);
}
console.log('items are: ', items);
console.log('retrieve filter', props.retrievedFilter);
console.log('retrieved items: ', retrievedItems);
};
useEffect(() => {
console.log('useEffect ', retrievedItems);
}, [retrievedItems]);
const deleteFilter = index => {
// props.retrievedFilter.splice(index, 1);
items.splice(index, 1);
setItems([...items]);
// setItems([...props.retrievedFilter, ...items]);
console.log(items);
};
const { filter } = formData;
const onChange = e => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const onSubmit = e => {
e.preventDefault();
addToFilter(filter);
// Passing filter data up (i.e: to components that use <Filter />)
props.filterData(filter);
//Close the Popover
document.body.click();
};
const popover = (
<Popover id="popover-basic">
<Form>
<Form.Group controlId="formGroupEmail">
<Form.Label>Add New Filter</Form.Label>
<Form.Control
type="text"
placeholder="New Filter"
name="filter"
onChange={e => onChange(e)}
/>
</Form.Group>
<Button variant="dark" type="submit" onClick={e => onSubmit(e)}>
Add
</Button>
</Form>
</Popover>
);
return (
<Fragment>
<label>
<p className="filter-title">{props.title}</p>
</label>
<div className={props.className ? props.className : 'filter'}>
{!props.retrievedFilter
? items.map((item, index) => {
return (
<div className="filter-text" key={index}>
{item}
<Button
className="filter-button"
size="sm"
onClick={() => deleteFilter(index)}
>
X
</Button>
</div>
);
})
: props.retrievedFilter.map((item, index) => {
return (
<div className="filter-text" key={index}>
{item}
<Button
className="filter-button"
size="sm"
onClick={() => deleteFilter(index)}
>
X
</Button>
</div>
);
})}
<OverlayTrigger
trigger="click"
placement="right"
rootClose
overlay={popover}
>
<p className="text-field">Type new one</p>
</OverlayTrigger>
</div>
</Fragment>
);
};
export default Filter;
however retrievedItems shows as an empty array in the console.
any help would be appreciated.
setState is async. You have to console.log inside an effect hook with the array as a parameter.
useEffect(() => console.log(retrieved_items), [ retrievedItems ])
The second parameter ensures that the effect fires in repose to a change in the values passed to it.
Per my comment, here is a code snippet that I think does what you want.
I couldn't get it running in SO but here's a codepen: https://codepen.io/anon/pen/PrYYmz?editors=1010 (watch the chrome console as you add items)
import React, {
Fragment,
useState,
useEffect
} from 'react';
const Filter = props => {
const [formData, setFormData] = useState({filter: ''});
const [items, setItems] = useState([]);
const [retrievedItems, setRetrievedItems] = useState([]);
const addToFilter = newFilter => {
let retrievedFilter = ["da vinci", "paris", "london"];
console.log('add', retrievedFilter);
if (retrievedFilter.length > 0) {
setRetrievedItems([...retrievedItems, retrievedFilter]);
retrievedFilter = 0;
setRetrievedItems([...retrievedItems, newFilter]);
} else {
setItems([...items, newFilter]);
}
console.log('items are: ', items);
console.log('retrieve filter', props.retrievedFilter);
console.log('retrieved items: ', retrievedItems);
};
useEffect(() => {
console.log('useEffect ', retrievedItems);
}, [retrievedItems]);
const deleteFilter = index => {
// props.retrievedFilter.splice(index, 1);
items.splice(index, 1);
setItems([...items]);
// setItems([...props.retrievedFilter, ...items]);
console.log(items);
};
const {filter} = formData;
const onChange = e => {
setFormData({ ...formData,
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
addToFilter(filter);
// Passing filter data up (i.e: to components that use <Filter />)
//props.filterData(filter);
//Close the Popover
document.body.click();
};
return (
<Fragment >
<label >
<p className = "filter-title" > {
props.title
} </p> </label> <
div className = {
props.className ? props.className : 'filter'
} > {!props.retrievedFilter ?
items.map((item, index) => {
return ( <
div className = "filter-text"
key = {index} > {item} <button className = "filter-button" size = "sm" onClick = {() => deleteFilter(index)}>X</button></div>
);
}) :
props.retrievedFilter.map((item, index) => {
return ( <div className = "filter-text" key = {index} > {item} <button className = "filter-button" size = "sm" onClick = {() => deleteFilter(index)} >X</button></div>);})} <input type = "text" placeholder = "New Filter" name = "filter" onChange = {e => onChange(e) }/>
<button variant = "dark" type = "submit" onClick = {e => onSubmit(e)} >Add</button>
</div>
</Fragment>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

Categories