Issue
I'm creating a todo-list with description, priority and date. I'm getting priority and description by using TextField which invokes {inputChanged}. {InputChanged} sets todo.description and todo.priority and the DatePicker's {handleDateChange} function should insert todo.date as a string into the state. After this, pressing a button calls {addTodo} which adds the entire todo state onto [todos] state array which I can then display and remove items from. I am able to display 2 of these, and delete items without issue.
However, I can't figure out how to set todo.date outside of {inputChanged} function using the DesktopDatePickers value. I can log the {selectedDate.toString()} to console and get a value in the format of "Thu, 24 Nov 2022 22:00:00 GMT"(based on which day, month and year I choose) but again, I have no idea how to add that as todo.date.
I also, whenever reading input for the first time from either of the textfields, get a warning "A component is changing an uncontrolled input to be controlled" I googled this a few times but assume it's because of a wrongly set initial state or has something to do with trying to alter only 2 of the 3 todo state objects. This warning doesn't seem to affect functionality as the parts of the code it warns about still function as expected, however tips related to this are also appreciated.
Hopefully some of this makes sense, as you can probably tell from the explanation and code below I'm a beginner, and some of the code snippets work but I honestly couldn't teach their functionality to anyone. Thanks for all answers in advance. (I've decided to exclude imports, but I'm certain those aren't causing the issue.)
`
function Todolist() {
const [todo, setTodo] = useState({description: '', date: '', priority:''});
const [todos, setTodos] = useState([]);
const gridRef = useRef();
const inputChanged = (event) => {
setTodo({...todo, [event.target.name]: event.target.value});
}
const addTodo = (event) => {
setTodos([...todos, todo]);
console.log(selectedDate)
}
const deleteTodo = () => {
if (gridRef.current.getSelectedNodes().length > 0){
setTodos(todos.filter((todo, index) =>
index !== gridRef.current.getSelectedNodes()[0].childIndex))
} else {
alert('Select row first')
}
}
const columns = [
{headerName: 'Date', field: 'date', sortable:true, filter: true, floatingFilter: true },
{headerName: 'Description', field: 'desc', sortable:true, filter: true, floatingFilter: true },
{headerName: 'Priority', field: 'prio', sortable: true, filter: true, floatingFilter: true}
]
const [selectedDate, setSelectedDate] = useState(null)
const handleDateChange = (newValue)=>{
setSelectedDate(newValue)
console.log(selectedDate)
}
return (
<div className='App'>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<TabApp/>
<Stack direction="row" spacing={2} justifyContent="center" alignItems="center">
<DesktopDatePicker
views={["day", "month","year"]}
label="Date"
inputFormat="MM/DD/YYYY"
value={selectedDate}
onChange={handleDateChange}
renderInput={(params) => <TextField {...params} />}
name="date"
/>
<TextField
label="Description"
variant="standard"
name="desc" value={todo.desc}
onChange={inputChanged}
/>
<TextField
label="Priority"
variant="standard"
name="prio" value={todo.prio}
onChange={inputChanged}
/>
<Button onClick={addTodo} variant="contained">Add</Button>
<Button onClick={deleteTodo} variant="contained">Delete</Button>
</Stack>
</LocalizationProvider>
<div
className="ag-theme-material"
style={{
height: '700px',
width: '80%',
margin: 'auto'
}}
>
<AgGridReact
animateRows={true}
ref={gridRef}
onGridReady = {params => gridRef.current = params.api}
rowSelection='single'
columnDefs={columns}
rowData={todos}>
</AgGridReact>
</div>
</div>
);
};
`
I tried several solutions, most boiling down to the idea of finding a way to set todo.date somehow. One way I tried was based on the functionality of {inputChanged}
const handleDateChange = (newValue)=>{
setSelectedDate(newValue.toString())
setTodo({...todo, [todo.date]: selectedDate})
}
this doesn't cause an error but the following is seen when calling console.log(todo): "":null date: "" desc: "asd" description: "" prio: "asd" priority: "" I honestly don't know what to make of that, other than that the values are printed twice and the first "date" just being null.
Solution
Currently when you run this line:
setTodo({...todo, [todo.date]: selectedDate});
It tries to get value of the todo.date (whis is an empty string) as the key and then assign the value (selectedDate) to the key("").
In order to fix this, you should give the key by yourself like this:
const handleDateChange = (newValue)=> {
const newSelectedDate = newValue.toString();
setSelectedDate(newSelectedDate)
setTodo({...todo, date: newSelectedDate})
}
And also about this warning: "A component is changing an uncontrolled input to be controlled", it's happening because the inital value of the selectedDate is null. Changing it to "(new Date()).toString()" will fix your warning.
const [selectedDate, setSelectedDate] = useState((new Date()).toString());
Answered By - Amirhossein
Answer Checked By - - Terry (ReactFix Volunteer)