Combine function came from ownProps in mapDispatchToProps - reactjs

In my react component, I want to be able to combine the standard actions and the functions I receive through ownProps? How do I do that? A follow up question is that in ownProps, I could get objects as well as functions. I want to make sure that I do not accidentally try to put an object into my actions. Only the functions should go into my actions.
So ultimately, I should be able to type this.props.actions.standardAction1 or this.props.actions.actionReceivedThruOwnProps.
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as standardActions from '../actions/standardActions';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div>Something goes here...</div>
);
}
}
function mapStateToProps(state) {
return {
something: state.something
}
}
function mapDispatchToProps(dispatch, ownProps) {
return {
actions: bindActionCreators(standardActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

you can do this but I'm not really sure on how the function exists in ownProps:
function mapDispatchToProps(dispatch, ownProps) {
return {
actions: {
...bindActionCreators(standardActions, dispatch),
...ownProps
}
};
}
check this too, if you want to separate actions from functions came from ownProps:
function mapDispatchToProps(dispatch, ownProps) {
return {
actions: bindActionCreators(standardActions, dispatch),
ownProps: ownProps,
}
};
}
and then you call it like that : this.props.ownProps.actionReceivedThruOwnProps

You have full control on how final props passed to you component will look like. react-redux allow you to do so using third argument to connect decorator/function.
Define function, which will receive mapped state from mapStateToProps, bound actions from mapDispatchToProps and own props passed to component from parent. Then, merge them as you wish.
function mapStateToProps(state) {
return {
something: state.something,
}
}
function mapDispatchToProps(dispatch, ownProps) {
return {
...bindActionCreators(standardActions, dispatch),
};
}
function mergeProps(stateProps, dispatchProps, ownProps) {
return {
...ownProps,
state: stateProps,
actions: {
...dispatchProps,
...ownProps.actions,
},
};
}
connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
)(SuperComponent)

Related

React redux dispatch not available

Originally I had this call at index.js to trigger the load of my main data:
const store = configureStore();
store.dispatch(doStuff());
Now I want to do the next step and load this data at page level (seems better).
I'm basing myself on this post by Gaearon at the Redux github:
I have this code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { PropTypes } from 'prop-types';
import { bindActionCreators } from 'redux';
import * as myActions from '../../actions/myActions';
import { MuiThemeProvider } from 'material-ui/styles';
let createHandlers = function(dispatch) {
let doStuff = function() {
dispatch(myActions.doStuff())
};
return {
doStuff,
// other handlers
};
}
class MyPage extends Component {
constructor(props, context) {
super(props, context);
this.handlers = createHandlers(this.props.dispatch);
//this.handlers.doStuff();
this.state = {
myStuff: []
}
}
render() {
return (
<MuiThemeProvider>
<div>...</div>
</MuiThemeProvider>
);
}
}
function mapStateToProps(state, ownProps) {
return {
// Set state
};
}
function mapDispatchToProps(dispatch) {
return {
// Set state
};
}
MyPage.propTypes = {
// My props
}
export default connect(mapStateToProps, mapDispatchToProps)(MyPage);
The issue
When I uncomment the line, I get this error:
TypeError: dispatch is not a function
let doStuff = function() {
dispatch(myActions.doStuff())
};
The (most important) difference that I see is that I do mapping:
export default connect(mapStateToProps, mapDispatchToProps)(MyPage);
What do I need to do to get this to work?
Likely something easy, but I don't see it.
Connect's react-redux docs explain that:
If you do not supply your own mapDispatchToProps function or object
full of action creators, the default mapDispatchToProps implementation
just injects dispatch into your component’s props.
When you don't pass mapDispatchToProps params to connect, react-redux passes dispatch as a prop to the wrapped component.
If you pass mapDispatchToProps to connect, the wrapped actions are passed instead of dispatch, and this.props.dispatch is undefined.
So if you need dispatch in your compon, refrain from using mapDispatchToProps, or wrap all your actions with dispatch inside mapDispatchToProps.
Oh boy ... I didn't need Gaearon's script at all. All I had to do is call the actions list from the constructor:
class MyPage extends Component {
constructor(props, context) {
super(props, context);
props.actions.doStuff();
this.state = {
myStuff: []
}
}
render() {
return (
<MuiThemeProvider>
<div>...</div>
</MuiThemeProvider>
);
}
}
Where this is the important line:
props.actions.doStuff();
Which is available because it's mapped in mapDispatchToProps:
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(loadOrderActions, dispatch)
};
}

dispatch is not defined on my functions using react and redux

I am trying to use react-redux-loading-bar to show a loading bar during fetching data from API servers, I don't use promise middleware so I decided to use it without, the example says do this
import { showLoading, hideLoading } from 'react-redux-loading-bar'
dispatch(showLoading())
// do long running stuff
dispatch(hideLoading())
And it gives me this.
Uncaught ReferenceError: dispatch is not defined
I had similar issues with other libraries and gave up that time, this time I want to actually understand how this works, so any info is greatly appreciated. Heres the code that causing the error, speicifc function and class names stripped.
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { showLoading, hideLoading } from 'react-redux-loading-bar'
import * as xxxxxActions from '../../actions/xxxxx'
class xxxxxx extends React.Component {
constructor(props) {
super(props)
this.handleclick = this.handleclick.bind(this)
}
handleclick(){
dispatch(showLoading())
asynchronousGetFunction( target_url, function (data) {
dispatch(hideLoading())
})
}
render() {
return <li onClick={this.handleclick}>yyyyyyy</li>
}
}
function mapStateToProps( state ){
return {
}
}
function mapDispatchToProps(dispatch, state) {
return {
xxxxxActions: bindActionCreators( xxxxxActions, dispatch )
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(xxxxxx)
Once you connect your component, dispatch becomes a prop. The same applies for xxxxxActions...
In that case, the handle would be:
handleclick(){
this.props.dispatch(...)
}
You need to pass dispatch function to your props:
function mapDispatchToProps(dispatch, state) {
return {
xxxxxActions: ....,
showLoading: function () {
dispatch(showLoading());
},
hideLoading: function () {
dispatch(hideLoading());
},
};
}
Then, use it in your component:
this.props.showLoading();
...
this.props.hideLoading();
You don't need use "dispatch" in components. Bind your functions with dispatch in mapDispatchToProps.
Read more about mapDispatchToProps.

react redux props undefined on event listener when using mapDispatchToProps

My first time using react/redux and I'm trying to bind a simple action addClick to the 'click' event, but when I click I receive the error:
Uncaught TypeError: Cannot read property 'props' of undefined
My (stripped down) code is:
import {addClick} from './actions'
const mapDispatchToProps = {addClick}
class App extends Component {
componentDidMount() {
document.addEventListener('click', this.props.addClick)
}
componentWillUnmount() {
document.removeEventListener('click', this.props.addClick)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
Before I had it implemented without mapDispatchToProps using an action creator defined within the class and bound to this in the constructor. But I thought part of the point of mapDispatchToProps was to bind the action creator to this (as well as wrapping it in a dispatch)
What am I missing?
Thanks!
From what I can tell from the documentation, the object short-hand (const mapDispatchToProps =
{addClick}) you use for mapDispatchToProps doesn't bind this to anything. It just sees to it that your addClick action creator gets called with dispatch. So that if you, in your component execute addClick(3), then that will result in a call looking like this dispatch(addClick(3)).
I'm not sure why your action creator would need access to this though. Can't you just pass it what ever data it needs as a parameter? So that the call in your component might look like
componentDidMount() {
const {addClick, someOtherProp} = this.props;
document.addEventListener('click', () => addClick(someOtherProp));
}
Do you use props inside addClick action?
Check this example:
import React from "react";
import { render } from "react-dom";
import { connect, Provider } from "react-redux";
import { createStore } from "redux";
function addClick(event) {
return {
type: "CLICK",
payload: `pageX: ${event.pageX} | pageY: ${event.pageY}`
};
}
const mapStateToProps = state => {
return {
clickXY: state
};
};
const mapDispatchToProps = { addClick };
class App extends React.Component {
componentDidMount() {
document.addEventListener("click", this.props.addClick);
}
componentWillUnmount() {
document.removeEventListener("click", this.props.addClick);
}
render() {
return (
<h1>
Click message: {this.props.clickXY}
</h1>
);
}
}
function clickReducer(state = "None", action) {
switch (action.type) {
case "CLICK": {
return action.payload;
}
default:
return state;
}
}
let store = createStore(clickReducer);
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
class Root extends React.Component {
render() {
return (
<Provider store={store}>
<AppContainer />
</Provider>
);
}
}
render(<Root />, document.getElementById("root"));
Link to editor
So bind works well in this code.

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

Correct place to load data in connected redux component

Where is the correct place to load data in a redux component?
Currently I have it this way.
Say I have this container component:
import { loadResultsPage } from '../actions/winratio-actions';
function mapStateToProps(state) {
const {
isFetching,
results
} = state;
return {
isFetching,
results
};
};
export default connect(mapStateToProps, {
loadResultsPage
})(WinRatio);
I then make a call in the wrapped component's componentWillMount lifecycle event:
export default class WinRatio extends Component {
componentWillMount() {
this.props.loadResultsPage();
}
render() {
return (
<div>
<h1>Win Ratio</h1>
</div>
);
}
};
Where should this call happen?
You can do it in your container component. If you use redux you probably use smart\dumb components strategy. Create a container where you use compose function from redux package and you can compose it like this:
export default compose(
connect(null, { loadData }), //this is your async action
doOnComponentMount(({props}) => props.loadData()),
)(MyDumbComponent)
and doOnComponentMount is:
function doOnComponentMount(cb) {
return (Component) => {
return class extends React.Component {
componentDidMount() {
cb(this);
}
render() {
return <Component {...this.props} />;
}
}
}
}

Resources