How to wrap multi actionCreators into one props? - reactjs

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

Related

Embedded Child Component in React is Undefined

I have the following parent component:
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
import _ from "lodash";
import ChildComponent from "./ChildComponent";
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
I'm at Parent
<ChildComponent/>
</div>
);
}
}
function mapStateToProps(state) {
return { }
}
export default connect(mapStateToProps, null)(ParentComponent);
Inside the parent has a component called ChildComponent that looks like this:
import React, { Component, PropTypes } from "react";
import { connect } from "react-redux";
import { reduxForm } from "redux-form";
import { bindActionCreators } from "redux";
class ChildComponent extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
}
render() {
return (
<div>
at the child
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
},
dispatch
);
}
function mapStateToProps(state) {
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(
ChildComponent
);
When I try adding the child component I keep getting this error:
But if I click continue the page turns back to normal. I don't understand how the child component is undefined. It's just embedded and does not include any props.
UPDATE:
I'm not getting the error anymore but I notice my page turns blank when I open this particular component. I'll be doing a bit more troubleshooting.
I tried out your code, and it works fine for me. My thought was maybe what your entry file looks like? or file structure? If you like you can try the following syntax for the parent and child - it worked this way as well:
Child:
const mapStateToProps = () => {
return {}
}
const ConnectedChildComponent = connect(
mapStateToProps,
{})(ChildComponent)
export default ConnectedChildComponent;
Parent:
const mapStateToProps = () => {
return {}
}
const ConnectedParentComponent = connect(
mapStateToProps,
{})(ParentComponent)
export default ConnectedParentComponent;
In your ParentComponent change:
import ChildComponent from "./ChildComponent";
to
import ChildComponent from "./ChildComponent.jsx";
i.e. add the missing ".jsx" extension. Your code is most likely determining the import to be a ".js" file by default, whereas it's actually a ".jsx" file.

this.props.dispatch is not a function in react js component file

In pagination 'onSelect' event I am calling an function that is define outside of render and in component's class. But when event firing below error coming -
BlogList.js:101 Uncaught TypeError: this.props.dispatch is not a function
here is my code snippit -
import React from 'react';
import StaticLayout from '../Layout/StaticLayout';
import { Link } from 'react-router-dom';
import { getBlogList } from '../actions/signupActions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import dateFormat from 'dateformat';
import { Pagination } from 'react-bootstrap';
import { push } from 'react-router-redux';
import queryString from 'query-string'
class BlogList extends React.Component {
constructor(props){
super(props);
document.title = "Blogs";
this.changePage = this.changePage.bind(this);
}
componentDidMount() {
this.props.getBlogList();
}
render(){
//===pagination variable========
const per_page = 1;
let pages = 0;
if(this.props.blogListData !== undefined){
pages = Math.ceil(this.props.blogListData.count / per_page) ;
}
const current_page = this.props.page;
const start_offset = (current_page - 1) * per_page;
let start_count = 0;
//===End pagination variable========
return(
<StaticLayout>
<blog list related html />
<Pagination className="users-pagination pull-right" bsSize="medium" maxButtons={10} first last next prev boundaryLinks items={pages} activePage={current_page} onSelect={this.changePage} />
</StaticLayout>
);
}
changePage(page){
alert(page);
this.props.dispatch(push('/?page_no='+page))
}
}
function mapStateToProps(state,ownProps){
var queryParam = queryString.parse(ownProps.location.search);
return {
blogListData: state.UserReducer.blogData,
page: Number(queryParam.page_no) || 1,
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({getBlogList: getBlogList}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps) (BlogList);
Plz let me know what i am doing wrong ?
dispatch is available to the component when you use connect only if you are not overriding it with the a custom function. which in your case is a mapDispatchToProps function. So what you can do is make the push action available as a prop to the component by adding it to the mapDispatchToProps function like
function mapDispatchToProps(dispatch) {
return bindActionCreators({getBlogList: getBlogList, push: push}, dispatch)
}
and use it like
changePage(page){
alert(page);
this.props.push('/?page_no='+page)
}
You can try hoisting the changePage into connect. I find this model easier to read and maintain.
function mapStateToProps(state,ownProps){
var queryParam = queryString.parse(ownProps.location.search);
return {
blogListData: state.UserReducer.blogData,
page: Number(queryParam.page_no) || 1,
}
}
const dispatch = (dispatch, ownProps) => ({
changePage: (page) => {
alert(page);
dispatch(push('/?page_no='+page))
}
});
export default connect(mapStateToProps, dispatch) (BlogList);

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.

Warning: Failed prop type: Game: prop type `game` is invalid; it must be a function, usually from React.PropTypes

import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
// import UI components
import GameList from '../components/game/GameList';
// import actions
import gameActions from '../actions/game';
const Game = (props) => {
const { game, actions } = props;
return (
<GameList game={game} actions={actions} />
);
};
Game.propTypes = {
game: PropTypes.shape.isRequired,
actions: PropTypes.shape.isRequired,
};
function mapStateToProps(state) {
return {
game: state.game,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(gameActions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Game);
I am trying to pass these two props as objects to a component and I am getting the invalid prop type error.
I need these two props to be objects, and I am pretty sure they are objects, why it needs them to be function?
The issue lies in your propTypes definition:
Game.propTypes = {
game: PropTypes.shape.isRequired,
actions: PropTypes.shape.isRequired,
};
for each you should be doing: PropTypes.shape({}).isRequired or PropTypes.object.isRequired
by just doing shape it's passing the shape function as the expectation.

How to use react-router-redux routeActions?

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'

Resources