Reputation: 38
I am setting up an extremely simple react-redux app, I only have one action type at the moment, and one reducer, so it's not an issue with using switch or combine reducers because there is only one option. In my reducer I have a boolean statement saying
if(action.type === ADD_ITEM)
then return (reducer code)
that reducer function is then called in my store. When I was testing my app the state was not updating, and through adding some console.logs to see where the problem is I realized that the reducer code is not being called because action.type === ADD_ITEM
is returning as false.
I tried console logging the action.type but I just got the default {type: @@redux/INITp.m.x.p.a.c}
which doesn't tell me much about why it's not reading the action.type as ADD_ITEM.
Btw: The action type is passed in import { ADD_ITEM } from "../constants/action-types";
and action-types contains this export const ADD_ITEM = "ADD_ITEM";
because I prefer using constants to strings for this.
I know action.type is not being called as "ADD_ITEM" but I don't know why and I'm running out of places to check. I've looked through the other questions similar to this on stack overflow but they all involved using switch for multiple action types, or using combine reducers, or having action.type as undefined. I only have one action and one reducer, and action.type is not coming back as undefined it's just not coming back as the action type that I set up to call the reducer function. Also, This is my first time using redux so please be kind.
action:
import { ADD_ITEM } from "../constants/action-types";
export function addArticle(payload){
return { type: ADD_ITEM, payload };
};
action-types.js : export const ADD_ITEM = "ADD_ITEM";
reducer:
import { ADD_ITEM } from "../constants/action-types";
const initialState = {
articles: []
};
function rootReducer(state = initialState, action){
if(action.type === ADD_ITEM){
console.log("yes");
return Object.assign({}, state, {
articles: state.articles.concat(action.payload)
});
}
console.log("no");
return state;
};
export default rootReducer;
store:
import { createStore } from "redux";
import rootReducer from '../reducers/index';
const store = createStore(rootReducer);
export default store;
form component that should be updating and calling dispatch with the article titles from the form input:
import React, { Component } from "react";
import { connect } from "react-redux";
import uuidv1 from "uuid";
import { addArticle } from "../actions/index";
function mapDispatchToProps(dispatch){
return {
addArticle: article => dispatch(addArticle(article))
};
}
class ConnectedForm extends Component {
constructor() {
super();
this.state = {
title: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
const { title } = this.state;
const id = uuidv1();
this.props.addArticle({ title, id });
this.setState({ title: "" });
}
render() {
const { title } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="title"
value={title}
onChange={this.handleChange}
/>
</div>
<button type="submit" className="btn btn-success">
SAVE
</button>
</form>
);
}
}
const Form = connect(null, mapDispatchToProps)(ConnectedForm);
export default Form;
the list where added articles should render:
import React from "react";
import { connect } from "react-redux";
const mapStateToProps = state => {
return { articles: state.articles };
};
const ConnectedList = ({ articles }) => (
<ul className="list-group list-group-flush">
{articles.map(el => (
<li className="list-group-item" key={el.id}>
{el.title}
</li>
))}
</ul>
);
const List = connect(mapStateToProps)(ConnectedList);
export default List;
index.js where I set the provider:
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import store from "./store/index";
import App from './components/App.jsx';
render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root')
);
the app where it comes together:
import React, { Component } from 'react';
import List from "./List";
import Form from "./Form";
class App extends Component {
render() {
return (
<div className="row mt-5">
<div className="col-md-4 offset-md-1">
<h2>Article List</h2>
<List />
</div>
<div className="col-md-4 offset-md-1">
<h2>Add a new article</h2>
<Form />
</div>
</div>
);
}
}
export default App;
I am expecting that when I input a title and submit the form it should call dispatch with that new 'article' add it to the state and then print it as part of the list component.
But, the part of the reducer that actually adds the new input onto the initial state is not running because action.type === ADD_ITEM
is returning as false.
Note: When I run this just in the console from a testing.js file with this:
import store from "../js/store/index";
import { addArticle } from "../js/actions/index";
window.store = store;
window.addArticle = addArticle;
I can run store.getState() in the console and it will return the empty array, then I can run something like
store.dispatch( addArticle({ title: 'What is happening?', id: 1 }) )
and it will add that to the array.
So that logic is working, but I think because when I'm running it with the form component and not directly calling store.dispatch it's relying on that action.type === ADD_ITEM
statement so none of the other logic is actually being called.
I just have no idea why the action.type is not being read as ADD_ITEM since in my action.js file I define the addArticle function with the type ADD_ITEM
any help would be appreciated
EDIT:
I forgot to mention, in the reducer if I console.log(addArticle())
it returns this
{type: "ADD_ARTICLE", payload: undefined}
payload: undefined
type: "ADD_ARTICLE"
__proto__: Object
so addArticle() does have the type "ADD_ARTICLE" but the action I'm passing to rootReducer does not..
Upvotes: 1
Views: 1787
Reputation: 360
I recommend using this browser extension https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd It will allow you to see if the action was fired, what was the payload of the action and what effect did it had on the state of the application. Great for debugging!
Upvotes: 1
Reputation: 38
Turns out when I was binding my handlers I had
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleChange.bind(this);
instead of
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
so handleSubmit wasn't being called. I had a feeling it'd be something simple like this
Upvotes: 0