Reusable components Redux - reactjs

I have a reusable component having its own action and reducer that i then use in another component.
Component AddToCart
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import Button from 'environment/atoms/button'
import * as AppIndexActionsCreators from 'environment/AppIndexActionsCreators'
const AddToCart = (props)=>{
let boundActionCreators = bindActionCreators(AppIndexActionsCreators)
return(
<Button
txt="Add To Cart"
{...boundActionCreators}
/>
)
}
export default AddToCart;
I pass it in
import React from 'react'
import { Link } from 'react-router'
import ProductDesc from '../Molecules/ProductDesc'
import ProductImg from 'environment/sharedMolecules/ProductImg'
import AddToCart from 'environment/sharedMolecules/AddToCart'
const Product = (props) => {
const product = props.product;
return (
<div>
<Link to={`/productDesc/${product.id}`}>
<ProductDesc {...props} />
<ProductImg {...props}
size="small"
/>
</Link>
<AddToCart/>
</div>
)
}
Product.propTypes = {
displayProduct: React.PropTypes.func,
product: React.PropTypes.object
};
On Click on AddToCart nothing happens where it should print a console.log as defined in my Reducer...when inspecting the AddToStore component in the browser i can see in the props that the component sees the AddToCart fn defined in the Action file......
looks like Action is not dispatched to the reducer...how to fix this ?

Use redux connect decorator.
Firstly, import it from react-redux bindings:
import { connect } from 'react-redux';
Then decorate your component with it!:
connect(mapStateToProps, mapDispatchToProps)(AddToCart)
Where functions in arguments should be defined somewhere like:
function mapStateToProps(state) {
return {
// someStoreVar: state.someStore.someStoreVar
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ ...AppIndexActionsCreators }, dispatch);
}
First one is to pass store state to props, second one is to pass
actions to props. They are fully optional and if you don't need the
store nor the actions bound to props you can omit them with null
like:
connect(null, mapDispatchToProps)(AddToCart)
Finally you will wan't this decorated component to be exported by
default instead of not-decorated one:
export default connect(mapStateToProps, null)(AddToCart)
At this moment you will be able to dispatch any actions or to access any store vars from props inside the component. This is a default technique to do this in react and you will use this quite alot. If this still sounds confusing feel free to ask for explanations.

I ended up rewriting my component as suggested by Syberic
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import Button from 'environment/atoms/button'
import * as AppIndexActionsCreators from 'environment/AppIndexActionsCreators'
const AddToCart = (props) => {
// let boundActionCreators = bindActionCreators(AppIndexActionsCreators)
return(
<Button
txt="Ajouter au panier"
clickAddToCart = { props.addToCart }
/>
)
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(AppIndexActionsCreators, dispatch);
}
export default connect(null,mapDispatchToProps)(AddToCart)
Additionally i corrected my Reducer where there was a syntax error. I did not put 'export default' but only 'export'
import { ADD_TO_CART } from './ActionTypes'
export default function cart(state=[],action){
console.log();
switch (action.type){
case ADD_TO_CART:
console.log('yeah');
return state;
default:
return state;
}
}

Related

dispatching an action from componentDidMount

I have to fetch some data just after the component is mounted in react.i am using componentDidMount method and inside that i want to dispatch an action but when i am calling popular() (see code) it does nothing and if i do dispatch(popular()) it showns error that dispatch and popula not defined
Please note that i have not used mapDispatchtoProps. What can i do to fetch the data ? Any help would be appreciated.
/action.js
export function popular(){
return dispatch=>{
return fetch(`https://api.themoviedb.org/3/movie/popular?api_key=<key>&language=en-US&page=1`)
.then(data=>data.json())
.then(data=>{
console.log("fetched",data);
dispatch({
type:"popular",
data:data.results,
page:data.page,
total_pages:data.total_pages,
total_results:data.total_results
})
})
}
}
/App.js
import React from 'react';
import {Header} from './header.js';
import {Searchbar} from './searchbar.js';
import {Thumbnails}from './Thumbnails.js';
import {connect} from 'react-redux';
import {Select} from './select.js';
import { Pagination } from './pagination.js';
import {Filterbar} from './filterbar.js';
import { popular } from '../redux/action.js';
class App extends React.Component {
componentDidMount(){
console.log("heloo");
popular();
}
render(){
return (
<>
<Header />
<Select {...this.props}/>
<Filterbar/>
<Searchbar {...this.props}/>
<Thumbnails {...this.props}/>
<Pagination {...this.props}/>
</>
);
}
}
function mapStateToProps(state){
return {
state:state
}
}
export default connect(mapStateToProps)(App)
To dispatch the popular action you need provide redux-thunk middleware to redux and pass popular action through mapDispatchToProps.
Here an example:
componentDidMount() {
this.props.getPopular();
}
....
function mapDispatchToProps(dispatch) {
return { getPopular: () => dispatch(popular()) };
}
Read more about that in the thunk docs.
Also I recommend you refresh basics of redux. Just because you have some confuse. Naming and usage tell me about that.

map action creator to props react redux

Problem statement: I have a class component in which I am trying to dispatch an action on click of a button.
Here is what the react class code looks like:
const mapDispatchToProps = dispatch => {
return {
// function that I want to call
handlerFunction(value){
dispatch(
// action creator
addProduct(value)
)
}
}
}
class Test extends Component {
state = {value: "Hello World"}
render() {
return (
<div>
<button onClick={()=>{this.handlerFunction(this.state.value)}}>Push me</button>
</div>
);
}
}
When I click on the button, I get the following error
TypeError: this.handlerFunction is not a function
Can anybody help me with why thats happening?
EDIT: I have used connect - importing it at the beginning of the file and doing connect(null, mapDispatchToProps)(Test) at the end
It is very difficult to provide complete example of react-redux as so many files you need to add to include redux in your project. But I mentioned here only basic example where you must be clear on action reducer to implement redux.
import React, {useEffect, useRef, useState} from 'react';
import {connect, useSelector} from 'react-redux';
import * as ServiceActions from './actions/service.actions';
import withReducer from 'app/store/withReducer';
import reducer from './reducers/service.reducer'
import {bindActionCreators} from "redux";
function TestDispatch(props) {
const {
handlerFunction
} = props;
return (
<div>
<button onClick={() => {
handlerFunction('Khabir');
}}>Push me
</button>
</div>
)
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
handlerFunction: ServiceActions.addProduct()
},
dispatch);
}
export default connect(null, mapDispatchToProps)(withReducer('service', reducer)(TestDispatch));

cant access to store in redux app

i want to use redux in my react native app
and i make my reducer correctly (i think ) and after that create store and then use dispatch or store but i get error actually i cant use dispatch
my app.js:
const store = createStore(rootReducer, applyMiddleware(logger));
export default class taav extends Component {
render() {
return (
<Provider store={store}>
<Chatroom />
</Provider>
);
}
}
and my chatroom :
import React, {Component} from 'react';
import {Text, Button, TabBarIOS, View} from 'react-native'
import MessageList from './messageList'
import {connect} from 'react-redux'
const mapStateToProps = (state) => {
return {
testtest: state.chatroom.teststore.test
}
}
export const Testredux = connect(mapStateToProps)(MessageList)
export default class Chatroom extends Component {
state = {
test2: "dfsd"
}
componentDidMount() {
console.log('this')
}
btn() {
dispatch({type: 'test1'})////////////this is wrong???
}
render() {
return (
<View>
<Testredux test={'sdfdsf'}/>
<Button title={'sdfsdf'} onPress={this.btn.bind(this)}/>
</View> )
}
}
do you know why i cant use dispatch????
You have not imported the 'dispatch' function anywhere so that's why you can't use it. You would have to import your store and then call store.dispatch().
However, the best (and recommended) way would be to use mapDispatchToProps with the connect function from react-redux when you export your Chatroom component:
function mapDispatchToProps(dispatch) {
return({
sendTestAction: () => { dispatch({type: 'test1'}) }
})
}
...and then use it with connect like this when you export your component:
connect(mapStateToProps, mapDispatchToProps)(Chatroom)
In your Chatroom component you can then do:
render() {
return (
<View>
<Testredux test={ 'sdfdsf' }/>
<Button title={ 'sdfsdf' } onPress={ this.props.sendTestAction }/>
</View>
)
}
I hope this helps.
You're connecting the wrong class
export const Testredux = connect(mapStateToProps)(MessageList)
should be
export const Testredux = connect(mapStateToProps)(Chatroom)
dispatch is a prop passed to the component wrapped in the connect call. Since you're wrapping MessageList instead of Chatroom, instances of Chatroom cannot access any props passed by Redux.
Additionally, dispatch should be accessed as a prop, so it should be this.props.dispatch instead of just dispatch:
btn() {
this.props.dispatch({type: 'test1'})
}

react-async-poll with a connected component

Looking at the docs for react-async-poll I'm following the Usage example to integrate asyncPoll into my component, but I'm getting a Uncaught TypeError: dispatch is not a function complaint from within my onPollinterval function
import React, { Component } from 'react';
import { connect } from 'react-redux';
import asyncPoll from 'react-async-poll';
import { fetchCaCities, } from '../actions';
import MyMap from './my-map';
class CaliforniaMap extends Component {
componentDidMount() {
this.props.fetchCaCities();
}
render() {
return (
<div>
<h1>California Map</h1>
<MyMap center={[37.5, -120]} zoom={6} layers={[this.props.caCities]} />
</div>
);
}
}
const onPollInterval = (props, dispatch) => {
console.log(dispatch); // undefined
return dispatch(fetchCaCities());
};
const mapStateToProps = state => ({
caCities: state.map.california.caCities,
});
export default asyncPoll(60 * 1000, onPollInterval)(connect(
mapStateToProps, { fetchCaCities }
)(CaliforniaMap)
Maybe react-async-poll doesn't work for connected components?
According to the docs:
The dispatch parameter is only passed to [onInterval] if it is
available in props, otherwise it will be undefined.
The example they give is confusing because it does not define dispatch anywhere, but they show onPollInterval using it.

Redux - reducer not getting called

I'm trying to learn Redux. I followed an online tutorial and used their example and it worked fine. Then I made another small test app and it worked fine. Now I'm trying another small test app and I'm stuck on this error and have no idea why.
The app is simply a input box and a button, and when you click the button it adds whatever's in the box to a list that is displayed below.
However, the reducer isn't updating the state. I've looked at the common problems that cause this and can't seem to find those errors in mine, my reducer doesn't change the state and I've bound the dispatch and so on. Maybe I've just mispelled something somewhere (or something small and stupid like that) but I just can't get it working.
So I tried to change it so that it just displays whatever you type in the box below and it STILL doesn't work. Anyone know why the reducer isn't activating?
index.js (main)
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import createLogger from 'redux-logger';
import allReducers from './reducers';
import App from './components/App';
const logger = createLogger();
const store = createStore(
allReducers,
applyMiddleware(thunk, promise, logger)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App.js
import React from 'react';
import NameList from '../containers/name-list.js'
import Input from '../containers/input.js'
const App = () => (
<div>
<Input />
<hr />
<NameList />
</div>
);
export default App;
index.js (action)
export const addName = (name) => {
return {
type: 'NAME_ADDED',
payload: name
}
};
reducer-add-name.js
export default function (state = null, action) {
switch (action.type) {
case 'NAME_ADDED':
return action.payload;
break;
}
return state;
}
index.js (reducer combiner)
import {combineReducers} from 'redux';
import AddNameReducer from './reducer-add-name';
const allReducers = combineReducers({
nameList: AddNameReducer
});
export default allReducers
input.js
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {addName} from '../actions/index'
class Input extends Component {
render() {
return(
<div>
<input id='name-input' />
<button id='add-name-button' onClick={() => addName(document.getElementById('name-input').value)}>Add name</button>
</div>
);
}
}
function mapStateToProps(state) {
return {
nameList: state.nameList
};
}
function matchDispatchToProps(dispatch) {
return bindActionCreators({addName: addName}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Input);
name-list.js
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
class NameList extends Component {
getNameList() {
//This is where I'll loop through this.props.nameList and build the list of elements
return this.props.nameList;
}
render() {
return(
<div>Current list : {this.getNameList()}</div>
);
}
}
function mapStateToProps(state) {
return {
nameList: state.nameList
};
}
export default connect(mapStateToProps)(NameList);
Thanks for any help!
I think your action hasn't been dispatched.
In your input.js
On button tag, change
onClick={() => addName(document.getElementById('name-input').value)}
to
onClick={() => this.props.addName(document.getElementById('name-input').value)}
Thing is action should be passed through mapDispatchToProps and 'bindActionCreators(actions, dispatch)' will wrap your action with 'dispatch', and pass through your component via props 'addName'. (as defined in your mapDispatchToProps)
An action alone is like a bullet (just return object) you will need something to fire it (dispatch)
In your case
import {addName} from '../actions/index' <--- a bullet
has been declared and your onClick, without dispatch, it would only return a mere object, not dispatching it to reducer.
addName() // <--- Just a bullet
But
this.props.addName() // <--- bullet with a gun
is from mapDispatchToProps / bindActionCreators... it has dispatch() wrapped around, that is the one we would use.
You should combine existing state with the new action payload in reducer-add-name.js instead of just returning the payload i.e.
return Object.assign({}, state, {
todos: [
...state,
{
action.payload
}
]
})
Not the OP's question but for people who just searched Google for "reducer not getting called", make sure you call your action creator:
dispatch(action())
not
dispatch(action)
You have to pass in the action, not the action creator. With useSelect in Redux Toolkit I usually don't have parens in the arg so I forgot to put the parents in the call to dispatch.
This happened to me today.
I forgot to add my new action to my destructured props in React while using the class method
const { newAction } = this.props

Resources