I'm learning react-redux-rails by building an application for a simple todo list. I'm finding myself getting increasingly confused with how certain parts of redux work and what it expects me to use. Currently, I'm trying to fetch a list of Todos from my DB and populate them as react components. I'm additionally trying to get accustomed to using functional components.
I have a single static page that renders a Provider with a store and App component
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('root');
const root = ReactDOMClient.createRoot(container)
root.render(
<Root store={store}/>
)
})
Then I have a set of actions for Todos
export const RECEIVE_TODOS = "RECEIVE_TODOS"
export const RECEIVE_TODO = "RECEIVE_TODO"
export const REMOVE_TODO = "REMOVE_TODO"
import * as APIUtil from '../util/todo_api_util'
export const receiveTodos = (todos) => {
return {
type: RECEIVE_TODOS,
todos,
}
}
export const receiveTodo = (todo) => {
return {
type: RECEIVE_TODO,
todo,
}
}
export const removeTodo = (todo) => {
return {
type: REMOVE_TODO,
todo,
}
}
export const fetchTodos = () => dispatch => (
APIUtil.fetchTodos().then(todos => dispatch(receiveTodos(todos)))
)
The last of these uses the thunk middleware (added to the store) and pulls from this simple API
export const fetchTodos = () => {
return(jQuery.ajax({
method: 'GET',
url: '/api/todos',
}))
}
Lastly, I have a TodoList Container
import { connect } from "react-redux";
import { receiveTodo } from "../../actions/todo_actions"
import TodoList from "./todo_list";
import { allTodos } from "../../reducers/selectors";
import { fetchTodos } from "../../util/todo_api_util";
const mapStateToProps = state => ({
todos: allTodos(state)
});
const mapDispatchToProps = dispatch => ({
receiveTodo: (todo) => dispatch(receiveTodo(todo)),
requestTodos: () => dispatch(fetchTodos())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
And the Todo List itself
import React, { useEffect } from "react";
import TodoForm from "./todo_form";
import TodoListItem from "./todo_list_item";
const TodoList = (props) => {
console.log(props)
useEffect(() => {
props.requestTodos();
})
return(
<div className="todo-list">
<TodoForm
receiveTodo={ props.receiveTodo } />
<ul className="todo-items">
{props.todos.map((todo, idx) => {
return (
<TodoListItem key={idx} todo={todo}
receiveTodo={ props.receiveTodo} />)
})}
</ul>
</div>
)
}
export default TodoList;
Now I thought that I understood useEffect for being a replacement of componentDidMount, but when I run this, I get the error as follows
I've tried reorganizing it in several ways (removing the '()', calling the store, etc), but can't get it to work. In addition, if I directly call store.dispatch(requestTodos()) in the console (with everything put onto the window), they will update the todos. Any ideas of what to do/why?
After researching functional components further, I needed to import { useDispatch } from react-redux to allow the store to update. I modified my Todo List component and it now works:
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import TodoForm from "./todo_form";
import TodoListItem from "./todo_list_item";
const TodoList = (props) => {
console.log(props)
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchTodos())
}, [])
return(
<div className="todo-list">
<TodoForm
receiveTodo={ props.receiveTodo } />
<ul className="todo-items">
{props.todos.map((todo, idx) => {
return (
<TodoListItem key={idx} todo={todo}
receiveTodo={ props.receiveTodo} />)
})}
</ul>
</div>
)
}
export default TodoList;