Reputation: 77
I'm doing an attemp of Todo App in React- Redux; got store, actions, reducer & Addodo component running; but got stucked at showing todos:
if my initialState=[] how can i retrieve data from store?
Store:
import { createStore } from 'redux';
import rootReducer from '../reducer';
export const store = createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store
My reducer:
const initialState = [];
const todos = (state = initialState, action) => {
switch(action.type) {
// Aca va tu codigo;
case 'AddTodo':
return [...state, action.payload];
case 'RemoveTodo':
return state.filter(todo => todo.id !== action.payload)
case 'ToInProgress':
return state.map((todo) =>
todo.id === action.payload ? { ...todo, status: "InProgress" } : todo
);
case 'ToDone':
return state.map((todo) =>
todo.id === action.payload ? { ...todo, status: "Done" } : todo
);
default:
return state
}
}
export default todos;
App.js:
import React from 'react'
import { Route } from "react-router-dom"
import {Provider} from 'react-redux'
import store from './store/index'
import Nav from '../src/components/Nav/Nav'
import Home from '../src/components/Home/Home'
import AddTodo from '../src/components/AddTodo/AddTodo'
import TodoDetail from '../src/components/TodoDetail/TodoDetail'
import './App.css';
export function App() {
return (
<React.Fragment>
<Provider store={store}>
<Nav />
<Route exact path="/" component={Home} />
<Route path="/add" component={AddTodo} />
<Route path="/edit/:id" component={TodoDetail} />
</Provider>
</React.Fragment>
);
}
export default App;
Home.js:
import React from 'react';
import Todos from '../Todos/Todos'
import './home.css'
export function Home() {
return (
<div className="todosContainer">
<div>
<Todos status="Todo"/>
<label>TODO</label>
</div>
<div>
<Todos status='InProgress' />
<label>InProgress</label>
</div>
<div>
<Todos status='Done'/>
<label>Done</label>
</div>
</div>
)
};
export default Home;
Here's AddTodo:
export function AddTodo() {
const [state,setState]=React.useState({
title:"",
description:"",
place:"",
date:"",
})
function handleChange(e){
const value=e.target.value;
setState({
...state,
[e.target.name]:value
})
}
function handleSubmit(e){
e.preventDefault();
store.dispatch(addTodo({
place:state.place,
title:state.title,
date:state.date,
description:state.description
})) // parameters "id" & "status" loaded in Actions.
setState({title:"",
description:"",
place:"",
date:"",})
}
return (
<div className="todoContainer">
<form id="formulario"onSubmit={handleSubmit} >
<label>
Title
<input type="text" name="title" value={state.title} onChange={handleChange} />
</label>
<label>
Description
<textarea type="text" name="description" value={state.description} onChange= {handleChange}/>
</label>
<label>
Place
<input type="text" name="place" value={state.place} onChange={handleChange}/>
</label>
<label>
Date
<input type="text" name="date" value={state.date} onChange={handleChange}/>
</label>
<button className="boton" type="submit" onClick={addTodo}>Agregar</button>
</form>
</div>
)
};
function mapDispatchToProps(dispatch){
return{
addTodo: todo => dispatch(addTodo(todo))
}
}
export default connect(mapDispatchToProps)(AddTodo)
Now, i want to show at Todos component, every Todos "title", still didn't know how to perform it:
export function Todos(data) {
return (
<div className="todoDetail">
<h4>{data.title}</h4>
</div>
)
};
function mapStateToProps(state) {
return {
data: state.title,
};
}
export default connect(mapStateToProps)(Todos)
Upvotes: 3
Views: 13257
Reputation: 202846
It's very odd they (code bootcamp) would require you to use a function component and then not allow you to use the React hooks that make the function components so appealing. No worries, you can still use the older connect
Higher Order Component to inject your redux state and dispatchable actions into the props passed to the component.
You've an issue in AddToDo
component. You are passing the mapDispatchToProps
to the connect
HOC where the mapStateToProps
callback would be passed. You also aren't using the injected addTodo
action you just mapped. You almost won't ever need to access the store
object directly to dispatch actions, use the React components/hooks.
export function AddTodo() {
const [state, setState] = React.useState({
title:"",
description:"",
place:"",
date:"",
});
function handleChange(e) {
const { value } = e.target;
setState(prevState => ({ // <-- use functional state update
...prevState,
[e.target.name]:value
}));
}
function handleSubmit(e) {
e.preventDefault();
this.props.addTodo({ // <-- addTodo action injected into props
place:state.place,
title:state.title,
date:state.date,
description:state.description
});
setState({
title:"",
description:"",
place:"",
date:"",
});
}
return (
<div className="todoContainer">
...
</div>
)
};
const mapDispatchToProps = {
addTodo,
}
export default connect(null, mapDispatchToProps)(AddTodo); // <-- mapStateToProps is null
In Todos
component, your todos state is an array, so you likely want to map it to JSX. Remember to destructure the passed props. Since you didn't share your reducers or how you combine your reducers I'm going to assume there is just one reducer for todos
located at the state root.
[update]
Since it seems your rootReducer
is just your todos
reducer function and I don't see where combineReducers
is used to build a more complex state tree, state
IS your todos
array state you are trying to pass to Todos
component.
[/update]
function Todos({ todos }) {
return (
<div className="todoDetail">
{todos.map((todo) => (
<h4 key={todo.id}>{todo.title}</h4>
))}
</div>
)
};
function mapStateToProps(state) {
return {
todos: state, // <-- state is todos array
};
}
export default connect(mapStateToProps)(Todos);
Upvotes: 1
Reputation: 311
You looking for useSelector hook.
Do not use store.getState()
in functional component. Also, you do not need to connect
functions components, use useDispatch hook.
Getting values from store will be like this:
const todos = useSelector(store => store.items);
Upvotes: 4