Doing a redux todo to learn and having trouble on the toggle todo.
When i trigger the onclick i get an error in the console.
"Failed prop type: The prop items is marked as required in ItemsList, but its value is undefined."
and
"Cannot read property 'length' of undefined" on items.length
I've consoled logged the action and it seems to be returning the state. not sure what i'm doing wrong. code is below.
actions
export const addItem = content => {
return { type: ADD_ITEM, content };
};
export const toggleTodo = (id) => {
return {
type: TOGGLE_TODO,
id,
};
};
my initial state is:
import { ADD_ITEM, TOGGLE_TODO } from './constants';
let nextId = 4;
export const initialState = {
items: [
{ id: 1, content: 'Call mum', completed: false},
{ id: 2, content: 'Buy cat food', completed: true },
{ id: 3, content: 'Water the plants', completed: false },
],
};
const reducer = (state = initialState, action) => {
console.log("action", action.type);
switch (action.type) {
case ADD_ITEM:
const newItem = {
id: nextId++,
content: action.content,
completed: false
};
return {
...state,
items: [...state.items, newItem],
};
case TOGGLE_TODO:
return state.items.map(todo => {
console.log("state", state);
console.log("state.items", state.items);
console.log("todo",todo);
if (todo.id !== action.id) {
return state;
}
return {
...state,
completed: !todo.completed,
};
});
default:
return state;
}
};
export default reducer;
and my list component is
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Todo from "../ItemTodo/index"
import { toggleTodo } from "../../logic/actions";
import './styles.css';
export const ItemsList = ({ items, onTodoClick }) => {
return (
<div>
<ul className={'itemsList-ul'}>
{items.length < 1 && <p id={'items-missing'}>Add some tasks above.</p>}
{items.map(item =>
<Todo
key={item.id}
{...item}
onClick={() => onTodoClick(item.id)}
/>
)}
</ul>
</div>
);
};
ItemsList.propTypes = {
items: PropTypes.array.isRequired,
onTodoClick: PropTypes.func.isRequired,
};
const mapStateToProps = state => {
return { items: state.todos.items };
};
const mapDispatchToProps = dispatch => ({
onTodoClick: id => dispatch(toggleTodo(id))
});
export default connect(mapStateToProps, mapDispatchToProps)(ItemsList);
and my todo component is
import React from 'react';
import PropTypes from 'prop-types';
const Todo = ({onClick, completed, content }) => (
<li
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none',
}}
>
{content}
</li>
);
Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
content: PropTypes.string.isRequired,
};
export default Todo;
Create store
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import configureStore from './redux/store';
import Header from './components/Header';
import ItemCreator from './components/ItemCreator';
import ItemsList from './components/ItemsList';
import './app.css';
const store = configureStore();
class App extends Component {
render() {
return (
<Provider store={store}>
<div className="app">
<Header />
<div>
<ItemCreator />
<ItemsList />
</div>
</div>
</Provider>
);
}
}
export default App;
import { createStore, applyMiddleware, compose } from 'redux';
import createReducer from './reducers';
const composeEnhancers =
(process.env.NODE_ENV !== 'production' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
const configureStore = (initialState = {}) => {
return createStore(
createReducer(),
initialState,
composeEnhancers(applyMiddleware())
);
};
export default configureStore;
import { combineReducers } from 'redux';
import reducer from '../logic/reducer';
export default function createReducer() {
return combineReducers({
todos: reducer,
});
}
I haven't tested it, but I think this part is incorrect TOGGLE_TODO:
return state.items.map(todo => {
console.log("state", state);
console.log("state.items", state.items);
console.log("todo",todo);
if (todo.id !== action.id) {
return state;
}
return {
...state,
completed: !todo.completed,
};
});
it should be:
return {
...state,
items: state.items.map((todo) => {
if (todo.id === action.id) {
return { ...todo, completed: !todo.completed };
}
return todo;
})
};
Related
I've created a simple to do app using React. I've attempted to persist state using local storage. However, the local storage code I've added is somehow preventing my components from rendering altogether. Not only are the todos saved in state not appearing, none of my components will render. I get a blank page on refresh. Can someone help me figure out what's wrong?
Here's what happens on the initial save after the local storage code is included. It loads the components just fine, but the to dos that are already in state are not shown:
After using the form to add to dos and refreshing the page, this happens. None of the components are shown whatsoever. Just a blank page.
Here is the local storage code inside my index.js file. I'm pretty sure the problem is here but I have included the code for the other components and the reducer as well:
const persistedState = localStorage.getItem('state') ? JSON.parse(localStorage.getItem('state')) : [];
const store = createStore(reducer, persistedState);
store.subscribe(() => {
localStorage.setItem('state', JSON.stringify(store.getState()));
})
The index.js file in its entirety:
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from "redux";
import { Provider } from "react-redux";
import './index.css';
import App from './App';
import { reducer } from "./reducers/todoReducer";
import * as serviceWorker from './serviceWorker';
const persistedState = localStorage.getItem('state') ? JSON.parse(localStorage.getItem('state')) : [];
const store = createStore(reducer, persistedState);
store.subscribe(() => {
localStorage.setItem('state', JSON.stringify(store.getState()));
})
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
the other components:
TodoList.js:
import Todo from "./Todo";
const TodoList = props => {
return (
<ul className="task-list">
{props.state.map(task => (
<Todo task={task} />
))}
</ul>
)
}
const mapStateToProps = state => {
return {
state: state
}
}
export default connect(mapStateToProps)(TodoList);
TodoForm.js:
const TodoForm = props => {
const [newItemText, setNewItemText] = useState("");
const handleChanges = e => {
e.preventDefault();
setNewItemText(e.target.value);
};
const saveState = () => localStorage.setItem("props.state", JSON.stringify(props.state));
useEffect(() => {
const todos = localStorage.getItem('state');
if (todos) props.setState({ [props.state]: JSON.parse(props.state) })
}, [])
return (
<div className="form-div">
<input
className="add-input"
name="todo"
type="text"
placeholder="enter a task"
value={newItemText}
onChange={handleChanges}
/>
<button
className="add-button"
onClick = {e => {
e.preventDefault();
props.addItem(newItemText);
saveState();
}}>Add a Task
</button>
<button
className="add-button"
onClick={e => {
e.preventDefault();
props.removeCompleted();
}}>Remove Completed
</button>
</div>
)
}
const mapStateToProps = state => {
return {
state: state
}
}
export default connect(mapStateToProps, {addItem, removeCompleted})(TodoForm);
Todo.js:
const Todo = props => {
return (
<li
className="tasks"
style={{textDecoration: props.task.completed ? 'line-through' : 'none'}}
onClick={() => props.toggleCompleted(props.task.id)}>
{props.task.item}
</li>
)
}
const mapStateToProps = state => {
return {
state: state
}
}
export default connect(mapStateToProps, {toggleCompleted})(Todo);
todoReducer.js:
export const initialState = [
{ item: 'Learn about reducers', completed: false, id: 1 },
{ item: 'review material from last week', completed: false, id: 2 },
{ item: 'complete reducer todo project', completed: false, id: 3 }
]
export const reducer = (state = initialState, action) => {
switch(action.type) {
case ADD_ITEM:
// console.log(action.payload)
return [
...state,
{
item: action.payload,
completed: false,
id: Date.now()
}
]
case TOGGLE_COMPLETED:
const toggledState = [...state];
toggledState.map(item => {
if(item.id === action.payload) {
item.completed = !item.completed;
}
})
console.log(toggledState);
state = toggledState;
return state;
case REMOVE_COMPLETED:
return state.filter(item => !item.completed);
default:
return state;
}
}
export default reducer;
App.js:
import React from 'react';
import './App.css';
// components
import TodoList from "./components/TodoList";
import TodoForm from "./components/TodoForm";
function App() {
return (
<div className="App">
<h1 className="title">To Do List</h1>
<TodoList />
<TodoForm />
</div>
);
}
export default App;
actions.js:
export const ADD_ITEM = 'ADD_ITEM';
export const TOGGLE_COMPLETED = 'TOGGLE_COMPLETED';
export const REMOVE_COMPLETED = 'REMOVE_COMPLETED';
export const addItem = input => {
return {
type: ADD_ITEM, payload: input
}
};
export const toggleCompleted = (id) => {
return {
type: TOGGLE_COMPLETED, payload: id
}
};
export const removeCompleted = () => {
return {
type: REMOVE_COMPLETED
}
};
I have to set a value on from a API into a newly created <button> component handled by Redux, but I don't know if I can use setState for this. I created a reducer and an action SET_VOTE_COUNT but I'm not seeing how this is done. This is my first Redux project, so here is the code:
// ./src/js/components/CounterList.js
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from '../actions/reducer';
import Counter from './Counter';
const CounterList = ({
counters,
onIncrement,
onDecrement
}) => (
<ul>
{counters.map(counter =>
<Counter style={{div: "voting"}}
key={counter.id}
value={counter.count}
onIncrement={() => onIncrement(counter.id)}
onDecrement={() => onDecrement(counter.id)}
/>
)}
</ul>
);
const mapStateToProps = (state) => {
return {
counters: state
};
};
const mapDispatchToProps = (dispatch) => {
return {
onIncrement: (id) => dispatch(increment(id)),
onDecrement: (id) => dispatch(decrement(id))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(CounterList);
// ./src/js/components/Counter.js
import React, { Component } from 'react';
class Counter extends Component {
render() {
return (
<div className="voting">
<span>{this.props.value}</span>
<button
onClick={() => this.props.onIncrement()}>
+
</button>
<button
onClick={() => this.props.onDecrement()}>
-
</button>
</div>
);
}
}
export default Counter;
import React, {Component} from 'react';
import logo from '../../logo.svg';
import '../../App.css';
import AddButton from './AddButton'
class Posts extends Component {
constructor(props) {
super(props);
this.state = {
response: ''
};
}
componentDidMount() {
fetch(
"/posts"
).then(response => response.json())
.then(data => this.setState({ response: data }))
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{Array.isArray(this.state.response) &&
this.state.response.map(resIndex => <>
{ resIndex.voteScore}
<AddButton className="voting"/>
<p> { resIndex.title }, by { resIndex.author } </p>
<p> { resIndex.body } </p>
<p> {resIndex.category} </p>
</>
)}
</header>
</div>
)
}
}
export default Posts;
import React from 'react';
import { add_counter, setVoteCount } from '../actions/reducer';
import { connect } from 'react-redux';
const AddButton = ({dispatch}) => (
<div className="voting">
<button
onClick={() => {
dispatch(setVoteCount())
// dispatch(add_counter());
}}>
Vote
</button>
</div>
);
export default connect()(AddButton);
The reducer:
// ./src/js/actions/counters.js
export const setVoteCount = (id) => {
return {
type: "SET_VOTE_COUNT",
id
};
}
export const increment = (id) => {
return {
type: "INCREMENT",
id
};
};
export const decrement = (id) => {
return {
type: "DECREMENT",
id
};
};
export const add_counter = () => {
return {
type: "ADD_COUNTER"
};
};
store action:
import { createStore, applyMiddleware, compose } from 'redux';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const change_counter = (state = {}, action) => {
switch (action.type) {
case "SET_VOTE_COUNT":
if (state.id !== action.id) {
return state;
}
return {
...state,
count : 37
}
case "INCREMENT":
if (state.id !== action.id) {
return state;
}
return {
...state,
count: state.count+1
};
case "DECREMENT":
if (state.id !== action.id) {
return state;
}
return {
...state,
count: state.count - 1
};
default:
return state;
}
};
let nextId = 0;
const counters = (state = [], action) => {
switch (action.type) {
case "ADD_COUNTER":
return [...state, {id: nextId++, count: 0}];
case "SET_VOTE_COUNT":
return [...state, {id: nextId++, count: action.count}];
case "INCREMENT":
return state.map(counter => change_counter(counter, action));
case "DECREMENT":
return state.map(counter => change_counter(counter, action));
default:
return state;
}
}
export default createStore(counters, composeEnhancers(applyMiddleware()));
I can upload it to GitHub if necessary. Many thanks.
In the AddButton component,the actions should be wrapped in mapDispatchToProps and passed to the connect function. You are calling the raw action in your example, but you need to wrap it with dispatch for it to update the store.
However, I'm not sure what you are trying to update the store with exactly. The action payload is empty in your example, and the reducer has 37 hardcoded as the state.count in response the SET_VOTE_COUNT action type. Did you mean to pass something from the API response?
<AddButton count={resIndex.count} className="voting"/>
import React from 'react';
import { add_counter, setVoteCount } from '../actions/reducer';
import { connect } from 'react-redux';
const mapDispatchToProps = {
setVoteCount
};
const AddButton = props => (
<div className="voting">
<button onClick={() => {
props.setVoteCount(props.count);
}}>
Vote
</button>
</div>
);
export default connect(null, mapDispatchToProps)(AddButton);
When I press the button increment or decrement, I get this message.
I think all the steps are okay but I can not understand why, when I press the button (+ or -), the store/state is set to undefined;
src/App.js
import React, { Component } from "react";
import Counter from "./components/counter";
import { connect } from "react-redux";
class App extends Component {
render() {
return (
<div>
{this.props.data.map(counter => (
<Counter key={counter.id} id={counter.id} value={counter.value} />
))}
</div>
);
}
}
const mapStateToProps = state => {
return {
data: state.data
};
};
export default connect(mapStateToProps)(App);
src/components/counter.js
import React, { Component } from "react";
import { connect } from "react-redux";
class Counter extends Component {
render() {
return (
<div>
<h1>{this.props.value}</h1>
<button onClick={() => this.props.onIcrement(this.props.id)}>
+ UP
</button>
<button onClick={() => this.props.onDecrement(this.props.id)}>
- DOWN
</button>
</div>
);
}
}
const mapDispatchToProps = dispatch => {
return {
onIcrement: id => dispatch({ type: "INCREMENT", key: id }),
onDecrement: id => dispatch({ type: "DECREMENT", key: id })
};
};
export default connect(
null,
mapDispatchToProps
)(Counter);
src/store/reducer.js
const initialState = {
data: [{ id: 1, value: 4 }, { id: 2, value: 0 }]
};
const reducer = (state = initialState, action) => {
const newState = { ...state };
switch (action.type) {
case "INCREMENT":
return newState.data.map(el => {
if (action.key === el.id) {
return el.value++;
}
return el.value;
});
case "DECREMENT":
return newState.data.map(el => {
if (action.key === el.id) {
return el.value--;
}
return el.value;
});
default:
return newState;
}
};
export default reducer;
src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { createStore } from "redux";
import { Provider } from "react-redux";
import reducer from "./store/reducer";
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
serviceWorker.unregister();
................................................................................
In your reducer, you are returning an array instead of an object with a key data which is an array.
The following code should work.
const reducer = (state = initialState, action) => {
switch (action.type) {
case "INCREMENT":
return {
data: state.data.map(el => { // <- return object with data instead of newState.data.map
if (action.key === el.id) {
return { ...el, value: el.value + 1};
}
return el;
})
};
case "DECREMENT":
return {
data: state.data.map(el => { // <- same here too
if (action.key === el.id) {
return { ...el, value: el.value - 1 };
}
return el;
})
};
default:
return state;
}
};
I have the following structure:
and a TodoList component as follow:
import React from 'react'
import PropTypes from 'prop-types'
import Todo from './Todo'
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map( (todo, i) => (
<Todo key={i} {...todo} onClick={() => onTodoClick(todo.id)} />
))}
</ul>
)
TodoList.propTypes = {
todos: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired
).isRequired,
onTodoClick: PropTypes.func.isRequired
}
export default TodoList
I am getting the following error:
this is my index in the reducers folder:
import { combineReducers } from 'redux'
import {
ADD_TODO,
TOGGLE_TODO,
SET_VISIBILITY_FILTER,
VisibilityFilters
} from '../actions/index'
const { SHOW_ALL } = VisibilityFilters
function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
default:
return state
}
}
const todoApp = combineReducers({
visibilityFilter,
todos
})
export default todoApp
The onTodoClick doesn't fire either in TodoList and it's defined here:
import { connect } from 'react-redux'
import { toggleTodo } from '../actions/index'
import TodoList from '../components/TodoList'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
case 'SHOW_ALL':
default:
return todos
}
}
const mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
I use middleware in redux ,but when I dispatch a action ,but get error in browser:
Uncaught TypeError: Cannot read property 'type' of undefined
code is here:
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import rootReducer from '../reducers/reducers'
import Immutable from 'immutable'
const loggerMiddleware = createLogger()
//const initialState=0
function configureStore() {
return createStore(
rootReducer,
{postsBySubreddit:{},selectedSubreddit:'reactjs'},
applyMiddleware(thunkMiddleware, loggerMiddleware)
)
}
export default configureStore
and my action :
import fetch from 'isomorphic-fetch'
import {actionCreator} from '../utils/creator'
const REQUEST_POSTS = 'REQUEST_POSTS'
const RECEIVE_POSTS = 'RECEIVE_POSTS'
const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT'
const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'
export function selectSubreddit(reddit) {
return {
type: SELECT_SUBREDDIT,
reddit
}
}
export function invalidateReddit(reddit) {
return {
type: INVALIDATE_REDDIT,
reddit
}
}
function requestPosts(reddit) {
console.log('requestPosts')
return {
type: REQUEST_POSTS,
reddit
}
}
function receivePosts(reddit, json) {
return {
type: RECEIVE_POSTS,
reddit: reddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
}
function fetchPosts(reddit) {
return dispatch => {
dispatch(requestPosts(reddit))
return fetch(`https://www.reddit.com/r/${reddit}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(reddit, json)))
}
}
function shouldFetchPosts(state, reddit) {
if(state.postsByReddit.hasOwnProperty(reddit)){
const posts = state.postsByReddit[reddit]
}else{
posts=false
}
if (!posts) {
return true
}
if (posts.isFetching) {
return false
}
return posts.didInvalidate
}
export function fetchPostsIfNeeded(reddit) {
return (dispatch, getState) => {
if (shouldFetchPosts(getState(), reddit)) {
return dispatch(fetchPosts(reddit))
}
}
}
my components:
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { selectSubreddit, fetchPostsIfNeeded, invalidateSubreddit } from '../actions/action'
import Picker from '../components/Picker'
import Posts from '../components/Posts'
class AsyncApp extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.handleRefreshClick = this.handleRefreshClick.bind(this)
}
componentDidMount() {
const { dispatch, selectedSubreddit } = this.props
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectedSubreddit !== this.props.selectedSubreddit) {
const { dispatch, selectedSubreddit } = nextProps
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
}
handleChange(nextSubreddit) {
this.props.dispatch(selectSubreddit(nextSubreddit))
}
handleRefreshClick(e) {
e.preventDefault()
const { dispatch, selectedSubreddit } = this.props
console.log(this.props);
dispatch(invalidateSubreddit(selectedSubreddit))
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
render() {
const { selectedSubreddit, posts, isFetching, lastUpdated } = this.props
console.log(this.props)
return (
<div>
<Picker value={selectedSubreddit}
onChange={this.handleChange}
options={[ 'reactjs', 'frontend' ]} />
<p>
{lastUpdated &&
<span>
Last updated at {new Date(lastUpdated).toLocaleTimeString()}.
{' '}
</span>
}
{!isFetching &&
<a href='#'
onClick={this.handleRefreshClick}>
Refresh
</a>
}
</p>
{isFetching && posts.length === 0 &&
<h2>Loading...</h2>
}
{!isFetching && posts.length === 0 &&
<h2>Empty.</h2>
}
{posts.length > 0 &&
<div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Posts posts={posts} />
</div>
}
</div>
)
}
}
AsyncApp.propTypes = {
selectedSubreddit: PropTypes.string.isRequired,
posts: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
lastUpdated: PropTypes.number,
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state) {
const { selectedSubreddit, postsBySubreddit } = state
console.log('1:'+postsBySubreddit)
const {
isFetching,
lastUpdated,
items: posts
} = postsBySubreddit[selectedSubreddit] || {
isFetching: true,
items: []
}
return {
selectedSubreddit,
posts,
isFetching,
lastUpdated
}
}
export default connect(mapStateToProps)(AsyncApp)
reducer:
import { combineReducers } from 'redux'
import {reducerCreator} from '../utils/creator'
import Immutable from'immutable'
import {SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT ,REQUEST_POSTS, RECEIVE_POSTS} from '../actions/action'
let initialState=Immutable.fromJS({isFetching: false, didInvalidate: false,items:[]})
function selectedSubreddit(action) {
switch (action.type) {
case SELECT_SUBREDDIT:
return action.subreddit
default:
return state
}
}
function postsBySubreddit(action) {
console.log(action)
switch (action.type) {
case INVALIDATE_SUBREDDIT:
case RECEIVE_POSTS:
case REQUEST_POSTS:
return state.merge({
[action.subreddit]: posts(state[action.subreddit], action)
})
default:
return state
}
}
function posts(state=initialState,action) {
switch (action.type) {
case INVALIDATE_SUBREDDIT:
return state.merge({
didInvalidate: true
})
case REQUEST_POSTS:
return state.merge({
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return state.merge({
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
})
default:
return state
}
}
const rootReducer = combineReducers({
postsBySubreddit,
selectedSubreddit
})
export default rootReducer
when I call fetchPostsIfNeeded,it call fetchPosts
,and then called dispatch(requestPosts(reddit)) but my reducer
can't handle this action ,and I print action in reducer on console ,it show undefined
Your reducers postsBySubreddit and selectedSubreddit receive the wrong parameters list;
they should be: postsBySubreddit(state=initialState,action) and selectedSubreddit(state=initialState,action)