How to use react-router-redux routeActions? - reactjs

I'm trying to modify the example code of react-router-redux.
https://github.com/rackt/react-router-redux/blob/master/examples/basic/components/Home.js
this is my Home.js
class Home extends Component {
onSubmit(props) {
this.props.routeActions.push('/foo');
}
}
I also have a mapDispatchToProps for it.
function mapDispatchToProps(dispatch){
return bindActionCreators({ routeActions },dispatch);
}
When i called onSubmit function, I got an error
Uncaught TypeError: this.props.routeActions.push is not a function
If i remove this.props in onSubmit, the key in the URL changed but its still on the same page.
From localhost:8080/#/?_k=iwio19 to localhost:8080/#/?_k=ldn1ew
Anyone know how to fix it?
Appreciate it.

I don't think routeActions are passed as props. What you want to do is this:
import { routeActions } from 'react-router-redux'
this.props.dispatch(routeActions.push('/foo'));

To provide a little more explicit answer and the answer to my own question in the comment.
Yes you can do
import { routeActions } from 'react-router-redux'
this.props.dispatch(routeActions.push('/foo));
However as I mentioned mapDispatchToProps will override this.
To fix this you can bind the routeActions like so:
import { bindActionCreators } from 'redux'
import { routeActions } from 'react-router-redux'
function mapDispatchToProps(dispatch) {
return {
routeActions: bindActionCreators(routeActions, dispatch),
}
}
export default connect(null, mapDispatchToProps)(YourComponent)
Now you can do: this.props.routeActions.push('/foo')
Just FYI this can be done even neater
function mapDispatchToProps(dispatch) {
return {
...bindActions({routeActions, anotherAction}, dispatch)
}
}

As for react-router-redux v4:
import { push } from 'react-router-redux';
export class ExampleContainer extends React.Component {
static propTypes = {
changeRoute: React.PropTypes.func,
};
function mapDispatchToProps(dispatch) {
return {
changeRoute: (url) => dispatch(push(url)),
dispatch,
};
}
}
export default connect(null, mapDispatchToProps)(ExampleContainer);
then:
this.props.changeRoute('/foo');

I was able to get it working by doing this
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { routeActions } from 'react-router-redux'
export const Cmpt = React.createClass({ //code })
function mapDispatchToProps(dispatch) {
return bindActionCreators({
push: routeActions.push,
//all your other action creators here
}, dispatch)
}
export const CmptContainer = connect({}, mapDispatchToProps)(Cmpt)
Then you can use push via props
this.props.push('some/cool/route')

More Concisely
For these examples, I'd curry the mapDispatchToProps function like so:
bindActionDispatchers.js
import { bindActionCreators } from 'redux'
/** curries mapDispatchToProps with bindActionCreators to simplify React action dispatchers. */
export default function bindActionDispatchers(actionCreators) {
if(typeof actionCreators === 'function')
return (dispatch, ownProps) => bindActionCreators(actionCreators(ownProps), dispatch)
return dispatch => bindActionCreators(actionCreators, dispatch)
}
Foo.js
import React from 'react'
import bindActionDispatchers from './bindActionDispatchers'
import { routeActions } from 'react-router-redux'
import * as appActions from './actions'
const Foo = ({ routeActions ...appActions }) => (
<div>
<button onClick={routeActions.push('/route')}>Route</button>
<button onClick={appActions.hello('world')}>Hello</button>
</div>
)
export default connect(null, bindActionDispatchers({ routeActions, ...appActions }))(Foo)
I packaged this into a lightweight npm package bind-action-dispatchers complete with unit tests and sanity checks. To use this version install with:
npm i -S bind-action-dispatchers
and import the function with:
import bindActionDispatchers from 'bind-action-dispatchers'

Related

React.js Unable to get property 'dispatch' of undefined or null reference

Issue Screen Shot
I am facing 'dispatch' of undefined issue when executing the application.
Whether I need to import any package to work with dispatch in React.js?
ProgramManager.js: This is my component this is where I am using dispatch to call action creator.
import React from 'react';
import { bindActionCreators } from 'redux';
import {connect} from 'react-redux'
import {postsActions,postsSelectors} from '../store/userList/index';
class ProgramManager extends React.Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
this.fetchPosts({});
}
fetchPosts(params) {
this.context.store.dispatch(postsActions.fetchPosts(params));
}
render() {
return (
<div className="right-container">
....
</div>
) }
}
function mapStatetoProps(state) {
debugger;
return {
//users: state.Users
params: postsSelectors.getParams(state),
posts: postsSelectors.getPosts(state),
};
}
export default connect(mapStatetoProps)(ProgramManager);
Store.js: This is my Redux Root Store file.
import { applyMiddleware, createStore, combineReducers, compose } from 'redux';
import { createEpicMiddleware } from 'redux-observable';
//import { hashHistory } from 'react-router';
import { routerMiddleware } from 'react-router-redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import rootEpic from './epics';
// const logger = createLogger({ collapsed: true });
const epicMiddleware = createEpicMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
epicMiddleware.run(rootEpic);
export default createStore(
rootReducer,
composeEnhancers(
applyMiddleware(
epicMiddleware,
logger,
// routerMiddleware(hashHistory),
thunk,
)
)
);
epics.js This is my root epic that combines all sub epic files
import { combineEpics } from 'redux-observable';
import { values } from 'lodash';
import * as postsEpics from './userList/epic';
export default combineEpics(
...values(postsEpics)
);
reducer.js This is my root reducer that combines all sub reducers.
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import posts from './userList/reducer';
export default combineReducers({
posts,
routing: routerReducer,
});
epic.js This the sub epic file that actually gets data from the server.
import { keyBy } from 'lodash';
import axios from 'axios';
import querystring from 'querystring';
import { Observable } from 'rxjs/Observable';
import { push } from 'react-router-redux';
import * as actionTypes from './actionType';
import * as postsActions from './actionCreator';
//import * as RequestModel from './request.model';
export function fetchPosts(action$) {
debugger;
alert('fetchPosts');
//RequestModel.entity = "sample";
return action$.ofType(actionTypes.FETCH_COLLECTION)
.map(action => action.payload)
.switchMap(params => {
return Observable.fromPromise(
axios.get(`http://localhost:8081/posts?${querystring.stringify(params)}`)
// axios.get('http://localhost:4040/admin/getTenantUsers')
).map(res => postsActions.fetchPostsSuccess(res.data, params));
});
}
actionCreation.js this is where the disptacher called to get the state data from post
import { keyBy } from 'lodash';
import * as actionTypes from './actionType';
export function fetchPosts(payload) {
debugger;
return { type: actionTypes.FETCH_COLLECTION, payload };
}
export function fetchPostsSuccess(posts, params) {
debugger;
const byId = keyBy(posts, (post) => post.id);
return {type: actionTypes.FETCH_COLLECTION_SUCCESS, payload: {byId, params}};
}
I have never used a context object to dispatch an action.
I think the best way to have an action ready to be dispatched is by using mapDispatchToProps as the second argument of your connect wrapper, it will bring your action as a prop.
import React from 'react';
import { bindActionCreators } from 'redux';
import {connect} from 'react-redux'
import {postsActions,postsSelectors} from '../store/userList/index';
class ProgramManager extends React.Component {
componentDidMount() {
this.props.fetchPosts();
}
render() {
return (
<div className="right-container">
....
</div>
) }
}
function mapStatetoProps(state) {
debugger;
return {
//users: state.Users
params: postsSelectors.getParams(state),
posts: postsSelectors.getPosts(state),
};
}
function mapDispatchToProps() {
return postsActions
}
export default connect(mapStatetoProps, mapDispatchToProps)(ProgramManager);
mapDispatchToProps is usually a function that simply returns a javascript object with your action creators like:
{
fetchPosts: postActions.fetchPosts,
...
}
That's why you can even go more direct (and no need to declare a mapDispatchToProps function):
export default connect(mapStatetoProps, postActions)(ProgramManager);
React-Redux provides dispatch when you connect your components. So you don't have to use the store in the context to dispatch actions. You can simply do this.props.dispatch(actionCreator()) within your connected components.
Note that it only provides dispatch to your component if you do not pass your own mapDispatchToProps. i.e., when you do
connect(mapStateToProps)(Component)
// or
connect()(Component)
// and not
connect(mapStateToProps, mapDispatchToProps)(Component)
If you provide mapStateToProps, however, you are expected to specify the action creators and wrap them around dispatch, so you don't need the dispatch for manual dispatches anymore.

How to use multiple multiActions in react/redux?

I am importing multiple multiactions into my component and using connect. I have import one multaction and it works just fine, but I can't figure out how to do multiple. Here is my code.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import * as actions from '../../actions/posts_actions';
import * as actionsIndex from '../../actions/index';
import * as actionsComments from '../../comments_actions';
function mapStateToProps({ posts }, ownProps) {
return { post: posts[ownProps.match.params.id] };
}
export default connect(mapStateToProps, actions)(ShowPosts);
You can pass create a mapDispatchToProps that uses bindActionCreators and pass that as a second argument to connect instead of actions.
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators(Object.assign({}, actions, actionsIndex,
actionsComments), dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(ShowPosts);

How to wrap multi actionCreators into one props?

I'm getting the following error:
Uncaught TypeError: this.props.dispatch is not a function
Here's my component:
import React from 'react';
import PropTypes from 'prop-types';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as jobTitleSkillsActions from '../../actions/jobTitleSkillsActions';
import SkillList from './SkillList';
import * as userPositionActions from '../../actions/userPositionActions';
class SkillPage extends React.Component {
componentDidMount() {
this.props.dispatch(userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id; this.props.dispatch(jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
.....
const mapStateToProps = state => {
return {
job_title_skills: state.job_title_skills,
user_positions: state.user_positions
};
};
function mapDispatchToProps(dispatch) {
return {
actions: {
userPositionActions: bindActionCreators(userPositionActions, dispatch),
jobTitleSkillsActions: bindActionCreators(jobTitleSkillsActions, dispatch),
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SkillPage);
What am I doing wrong here?
Per my comment, this would be the correct syntax to bind multiple sub-objects worth of action creators:
function mapDispatchToProps(dispatch) {
return {
userPositionActions : bindActionCreators(userPositionActions, dispatch),
jobTitleSkillsActions: bindActionCreators(jobTitleSkillsActions, dispatch),
}
}
Since you have already used mapDispatchToProps, dispatch wont be available to the component as a prop. Since you have used mapDisptachToProps, the jobs actions will be available as props and you can use them like
componentDidMount() {
this.props.actions.userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id;
this.props.actions.jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
However you can simplify it further like
function mapDispatchToProps(dispatch) {
return bindActionCreators({userPositionActions, jobTitleSkillsActions}, dispatch)
}
...
componentDidMount() {
this.props.userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id;
this.props.jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
As you have provided mapDispatchToProps to connect function, dispatch is not passed as prop to your component.
Your componentDidMount code should be like this:
componentDidMount() {
const actions = this.props.actions
actions.userPositionActions.loadUserPositions()
var job_title_id = this.props.user_positions[0].job_title_id;
actions.jobTitleSkillsActions.loadJobTitleSkills(job_title_id)
}

How to use .dispatch in react redux?

I have the following
CatsPage.js:
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
//import * as catActions from '../../actions/catActions';
import CatList from './CatList';
import {loadCats} from '../../actions/catActions';
class CatsPage extends React.Component {
componentDidMount () {
this.props.dispatch(loadCats())
}
render() {
return (
<div>
<h1>Cats</h1>
<div>
<CatList cats={this.props.cats} />
</div>
</div>
);
}
}
CatsPage.propTypes = {
cats: PropTypes.array.isRequired
};
function mapStateToProps(state, ownProps) {
return {
cats: state.cats
};
}
export default connect(mapStateToProps)(CatsPage);
catActions.js
import * as types from './actionTypes';
import catApi from '../api/CatsApi';
export function loadCats() {
return function(dispatch) {
return catApi.getAllCats().then(cats => {
dispatch(loadCatsSuccess(cats));
}).catch(error => {
throw(error);
});
};
}
export function loadCatsSuccess(cats) {
return {type: types.LOAD_CATS_SUCCESS, cats};
}
I'm getting the following error:
Uncaught Error: Actions must be plain objects. Use custom middleware for async actions. Uncaught Error: Actions must be plain objects. Use custom middleware for async actions. at dispatch (createStore.js:166)
I'm a newbie try to learn how to use React + Redux. What am I doing wrong that I need to fix to make the dispatch work and loadCats()?
Thank you!
There's a chance you didn't properly install/configure your store. It should look something like this:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
// Note: this API requires redux#>=3.1.0
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);

Reusable components Redux

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;
}
}

Resources