I'm trying to learn Typescript by doing something simple like a Todo-List application. The problem is that I cannot update the Redux state array in a slice that I have created.(or more like I don't know how to)
Here is my main component Todolist.tsx
:
import React, { FC, ChangeEvent, FormEvent, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { update } from '../todoSlice';
import { RootState } from '../todoStore';
const TodoList: FC = () => {
// React state array for the todos
const [todos, setTodos] = useState<string[]>([]);
// Get the input from this state
const [input, changeInput] = useState<string>('');
const dispatch = useDispatch()
// Selecting the state of the todoSlice component
const selector = useSelector((state: RootState) => { return state.todoCard })
const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
event.preventDefault();
changeInput(event.target.value);
}
const addTodos = (event: FormEvent<HTMLFormElement>): void => {
event.preventDefault();
setTodos([...todos, input]);
dispatch(update(todos));
console.log("REACT: ", todos);
console.log("REDUX: ", selector);
}
const removeTodos = (index: number): void => {
let filteredArray = todos.filter((todo, todoIndex) => (
todoIndex !== index
))
setTodos(filteredArray);
}
return (
<React.Fragment>
<div className='bg-light d-inline-block text-center mt-4' style={{ padding: '10vh', marginLeft: '4vw' }}>
<h1 className='mb-4'>Todo List</h1>
<section className='text-wrap mb-4' style={{ overflowY: 'auto', height: '10rem' }}>
{
// Mapping thee todo-list items
todos.map((todo, index) => (
<React.Fragment key={index}>
<div className='d-flex justify-content-between mb-3'>
<li className='d-block'>{todo}</li>
<button onClick={() => {removeTodos(index)}}>X</button>
</div>
</React.Fragment>
))
}
</section>
<form onSubmit={addTodos}>
<input type='text' name="input" className='p-1 rounded border-success' onChange={handleChange}></input>
<button type="submit" className='ms-3 btn btn-success mb-1'>Enter</button>
</form>
</div>
</React.Fragment>
)
}
export default TodoList
and here is my slice component todoSlice.ts
:
import { createSlice } from '@reduxjs/toolkit'
export const todoSlice = createSlice({
name: 'todo',
initialState: {
todoCard: [],
},
reducers: {
update: (state, action) => {
return {
// Return a copy of the array and SHOULD update the state array here "immutably"
...state,
todoCard: [
...state.todoCard,
action.payload
]
}
}
}
})
export const { update } = todoSlice.actions
export default todoSlice.reducer
I have tried many different ways from Google but none of their methods seem to work in my problem. Thank you for the responses in advance! :)
EDIT: I forgot to add the errors shown so here it is
CodePudding user response:
export const todoSlice = createSlice({
name: 'todo',
initialState: {
todoCard: [],
},
reducers: { // <------- "Map Object" Notation
update: (state, action) => {
By doing this you're using https://redux-toolkit.js.org/api/createReducer with the "Map Object" Notation. This is what the redux-toolkit docs say about it:
While this notation is a bit shorter, it works only in JavaScript, not TypeScript and has less integration with IDEs, so we recommend the "builder callback" notation in most cases.
So I'd recommend to rewrite this with https://redux-toolkit.js.org/api/createReducer#usage-with-the-builder-callback-notation - you also don't need to worry about mutability then.
It also looks like there might be some type annotations missing. If state.todoCard
is an array of strings, it should be annotated as such. There are lots of examples in the redux-toolkit docs to get better TS support. Check out https://redux-toolkit.js.org/tutorials/typescript for example. Among other stuff, there is a version of useSelector
that already knows how your redux state is typed, this helps a lot.
CodePudding user response:
You need typings, your action is of type PayloadAction<string[]>, try this
reducers: {
update: (state, action: PayloadAction<string[]>) => {
...
You should give a type to your initialState too
interface InitialState {
todoCard: string[];
}
const init: InitialState = {
todoCard: [],
}
export const todoSlice = createSlice({
name: 'todo',
initialState: init,