I'm working on a react-redux app and for some reason the action I call does not reach the reducer (in which I currently only have a log statement). I have attached the code I feel is relevant and any contributions would be highly appreciated.
Action called within function in component:
onSearchPressed() {
console.log('search pressed');
this.props.addToSaved();
}
actions/index.js:
var actions = exports = module.exports
exports.ADD_SAVED = "ADD_SAVED";
exports.addToSaved = function addToSaved() {
console.log('got to ADD_SAVED step 2');
return {
type: actions.ADD_SAVED
}
}
reducers/items.js:
const {
ADD_SAVED
} = require('../actions/index')
const initialState = {
savedList: []
}
module.exports = function items(state = initialState, action) {
let list
switch (action.type) {
case ADD_SAVED:
console.log('GOT to Step 3');
return state;
default:
console.log('got to default');
return state;
}
}
reducers/index.js:
const { combineReducers } = require('redux')
const items = require('./items')
const rootReducer = combineReducers({
items: items
})
module.exports = rootReducer
store/configure-store.js:
import { createStore } from 'redux'
import rootReducer from '../reducers'
let store = createStore(rootReducer)
EDIT: Entire component for onSearchPressed:
class MainView extends Component {
onSearchPressed() {
this.props.addToSaved();
}
render() {
console.log('MainView clicked');
var property = this.props.property;
return (
<View style={styles.container}>
<Image style={styles.image}
source={{uri: property.img_url}} />
<Text style={styles.description}>{property.summary}</Text>
<TouchableHighlight style = {styles.button}
onPress={this.onSearchPressed.bind(this)}
underlayColor='#99d9f4'>
<Text style = {styles.buttonText}>Save</Text>
</TouchableHighlight>
</View>
);
}
}
module.exports = MainView;
Reducers, as the name suggests, take in two things: previous state and an action. Then they reduce it (read it return) to one entity: the new updated instance of state. So reducers are basically pure JS functions which take in the previous state and an action and return the newly updated state.
Dispatching an action within a reducer is an anti-pattern. Your reducer should be without side effects, simply digesting the action payload and returning a new state object. Adding listeners and dispatching actions within the reducer can lead to chained actions and other side effects.
Redux includes a utility function called bindActionCreators for binding one or more action creators to the store's dispatch() function. Calling an action creator does nothing but return an object, so you have to either bind it to the store beforehand, or dispatch the result of calling your action creator.
It doesn't work because your action creator is just a function that returns an action. It is up to you to actually dispatch it. We can't bind your action creators to a particular Store instance during the definition because apps that render on the server need a separate Redux store for every request.
Recently I faced issue like this and found that I had used action import but it has to come from props. Check out all uses of toggleAddContactModal. In my case I had missed toggleAddContactModal from destructuring statement which caused this issue.
import React from 'react'
import ReactDOM from 'react-dom'
import Modal from 'react-modal'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import {
fetchContacts,
addContact,
toggleAddContactModal
} from '../../modules/contacts'
import ContactList from "../../components/contactList";
Modal.setAppElement('#root')
class Contacts extends React.Component {
componentDidMount(){
this.props.fetchContacts();
}
render(){
const {fetchContacts, isFetching, contacts,
error, isAdding, addContact, isRegisterModalOpen,
toggleAddContactModal} = this.props;
let firstName;
let lastName;
const handleAddContact = (e) => {
e.preventDefault();
if (!firstName.value.trim() || !lastName.value.trim()) {
return
}
addContact({ firstName : firstName.value, lastName: lastName.value});
};
return (
<div>
<h1>Contacts</h1>
<div>
<button onClick={fetchContacts} disabled={isFetching}>
Get contacts
</button>
<button onClick={toggleAddContactModal}>
Add contact
</button>
</div>
<Modal isOpen={isRegisterModalOpen} onRequestClose={toggleAddContactModal}>
<input type="text" name="firstName" placeholder="First name" ref={node =>
(firstName = node)} ></input>
<input type="text" name="lastName" placeholder="Last name" ref={node =>
(lastName = node)} ></input>
<button onClick={handleAddContact} disabled={isAdding}>
Save
</button>
</Modal>
<p>{error}</p>
<p>Total {contacts.length} contacts</p>
<div>
<ContactList contacts={contacts} />
</div>
</div>
);
}
}
const mapStateToProps = ({ contactInfo }) => {
console.log(contactInfo)
return ({
isAdding: contactInfo.isAdding,
error: contactInfo.error,
contacts: contactInfo.contacts,
isFetching: contactInfo.isFetching,
isRegisterModalOpen: contactInfo.isRegisterModalOpen
});
}
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
fetchContacts,
addContact,
toggleAddContactModal
},
dispatch
)
export default connect(
mapStateToProps,
mapDispatchToProps
)(Contacts)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With