React redux dispatch not available - reactjs

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

Related

React, Redux - pass function from component A to other components

import React from "react";
import OtherComponent from "./OtherComponent";
class Main extends React.Component {
constructor(props) {
super(props);
this.runMyFunction = this.runMyFunction.bind(this);
this.myFunction = this.myFunction.bind(this);
}
runMyFunction(event) {
event.preventDefault();
this.myFunction();
}
myFunction() {
return console.log("I was executed in Main.js");
}
render() {
return (
<div>
<OtherComponent runMyFunction={this.runMyFunction} />
</div>
);
}
}
export default Main;
import React from "react";
class OtherComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.runMyFunction();
}
render() {
return (
<div>
<button onClick={this.handleClick} />Click me to execute function from Main </button>
</div>
);
}
}
export default OtherComponent;
I'm new in redux and don't know how to pass and run that function in other component. It was easy not using redux, just pass as props like in example above.
I have folder with actions, components, containers and reducers.
Now I have Main.js where I have
import React from "react";
const Main = ({data, getData}) => {
const myFunction = () => {
return "ok";
};
return (
<div>
<p>This is main component</p>
</div>
);
};
export default Main;
In MainContainer.js I got:
import Main from "../../components/Main/Main";
import { connect } from "react-redux";
import {
getData
} from "../../actions";
function mapStateToProps(state) {
return {
data: state.main.data
};
}
const mapDispatchToProps = (dispatch) => {
return {
getData: () => dispatch(getData())
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Main);
So how I can run function myFunction() in OtherComponent.js:
import React from "react";
const OtherComponent = ({executeFunctionInMainComponent}) => {
return (
<div>
<button onClick={executeFunctionInMainComponent}>run action</button>
</div>
);
};
export default OtherComponent;
I need to just run, not pass whole function, just execute myFunction in Main.js but action to run this function will came from OtherComponent.
So first i have to mention that i believe that you have a misconception of redux. This isn't to allow for functions created in components to be reused in different locations. This is to move that logic to a reducer outside of your function which would allow it to be used wherever you wired it with {connect} from react-redux. So you will need a couple of files (for clarity). First you're going to need an action file which we'll name myReturnOkAction.
export const myReturnOkAction = (/*optional payload*/) => {
return {
type: 'PRINT_OK',
}
}
Redux Actions
This is what you're going to call in your mapDispatchToProps function where you're going to trigger this event. You're going to have to import it into your OtherComponent so import {myReturnOkAction} from "/*wherever the files exists*/" and to include it in your mapDispatchToProps as okFunction: () => dispatch(myReturnOkAction())
Once you have your action your connect Higher Order Component (HOC) wrapping your main component is going to need a Reducer to modify your current store state as well as do any actions.
export const myReturnOkReducer = (state, action) => {
if(action.type === 'PRINT_OK'){
/*This is where you update your global state*/
/*i.e. return {...store, valueWanted: ok}*/
}else{
return state
}
}
Redux Reducers
So the way that this is going to move is that you're function, somewhere is going to call the action. Once the action is called its going to trigger the reducer and make any changes to the store which you need. Once the reducer has updated the store with new values its then going to update any components which are connected to it through the connect HOC which will cause them to re-render with new information.
Also my favorite image to describe how redux works.
I hope this helps.
I found an answer:
I still can pass as props in redux but I can't do this in this way: OtherComponent = ({executeFunctionInMainComponent}) => {}. I need to do in this way: OtherComponent = (props) => {} and then inside that component I have an access via props.executeFunctionInMainComponent

How to trigger callback function in Redux?

React and Redux experts.
I am new to React and Redux. My question is related to trigger callback (function) invocation when a Redux state is changed. I am stuck into this implementation. In my understanding, the presenter/view is updated via the props. Let me illustrate more in the following example.
<ParentComponent>
<Child1Component/>
<Child2Component/>
</ParentComponent>
class ParentComponent extends Component {
onChild1Click() {
this.props.dispatch(setTool(Tools.CHILD1TOOL))
}
render() {
return (
<div>
<Child1Component onChild1Click={this.onChild1Click.bind(this)}/>
<Child2Component/>
</div>
)
}
}
const mapStateToProps = state => {
return {state}
}
export default connect(
mapStateToProps
)(ParentComponent)
class Child1Component extends Component {
componentDidUpdate() {
// Question: How to get the Redux state here?
}
render() {
return (
<button onClick={this.props.onPencilClick}>Pencil</button>
)
}
}
Suppose a button is present in the Child1Component and a onclick is attached to such button. In my understanding of Redux, an action should be attached to this onclick and it should be dispatched. Such state will be modified in the ParentComponent and trigger props update. Afterwards, the UI/Presenter of Child1Component will be updated via props instead of any callback of Child1Component.
Is it possible to trigger a callback in Child1Component when a state is altered? The reason I need to make such implementation is that a 3rd party library is adopted. It requires to trigger callback. Actually, the onclick can trigger the function (callback) directly. However, the state cannot be maintained.
Could any expert advise it, please? Thanks a million.
P.
As I understand, this is not directly related to redux. You can use the react life cycle methods for this purpose. In your case, I think you need the componentDidUpdate or componentWillUpdate methods.
You can read more about life cycle methods here,
https://reactjs.org/docs/react-component.html
Explanation
First, make sure that you have connected the components to the Redux store using the react-redux bindings. Then, if you have correctly defined the mapStateToProps function, your child component will update whenever the state changes. Thus, whenever the component is updated, the componentWillUpdate and componentDidUpdate methods will be called.
Example in ES6 style
First, we'll bind the full redux state to the child component. Note: Generally you would not bind the full state, but only a branch of it.
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import ChildComponent from './ChildComponent';
function mapStateToProps(state) {
return {
// this will bind the redux state to the props of the child component
reduxState: state
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
// some action creators
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChildComponent);
Then we can access the redux state from the child component.
class ChildComponent extends React.Component {
componentWillMount(nextProps, nextState) {
// Do something here
console.log(this.props.reduxState)
// this.props.reduxState is accessible from anywhere in the component
}
render() {
return <div>{/*Some jsx here*/}</div>
}
}
I strongly recommend you to read about redux usage with react section from redux docs and about smart-dumb component separation
First off, thank you for the replies. I came up the solution eventually. Here it is.
// actions.js
export const SET_TOOL = 'SET_TOOL'
export const Tools = {
CHILD1TOOL: 'child1tool',
DEFAULT: 'default'
}
export function setTool(tool) {
return {
type: SET_TOOL,
tool
}
}
// reducers.js
import { combineReducers } from 'redux'
import { SET_TOOL, Tools } from './actions'
const { DEFAULT } = Tools
function currentTool(state = DEFAULT, action) {
switch(action.type) {
case SET_TOOL:
return action.tool
default:
return state
}
}
const myApp = combineReducers({
currentTool
})
export default myApp
// ParentComponent.jsx
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Tools, setTool } from './actions'
import Child1Component from './Child1Component.jsx'
class ParentComponent extends Component {
render() {
return (
<div>
<Child1Component onChild1Click={this.props.onChild1Click'}/>
</div>
)
}
}
const mapStatesToProps = state => {
return {state}
}
const mapDispatchToProps = dispatch => {
return {
onChild1Click: () => {
dispatch(setTool(Tools.CHIDL1TOOL))
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ParentComponent)
// Child1Component.jsx
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Tools } from './actions'
class Child1Component extends Component {
componentDidUpdate() {
if (this.props.state.currentTool === Tools.CHILD1TOOL) {
this.callbackHandleClick()
}
}
render() {
return <button onClick={this.props.onChild1Click}>Child 1 Button</button>
}
callbackHandleClick() {
/* callback implementation */
}
}
const mapStateToProps = state => {
return {state}
}
export default connect(
mapStateToProps
)(Child1Component)

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.

Redux error: mapDispatchToProps throws error

I am new to react-redux and I am having some difficulty in understanding the syntax. I am pasting my sample code below... please help me understand if there are any syntactical errors.
SampleParent.js:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { fetchNames, fetchDownloadLink } from '../../actions/actions'
import SampleChild from '../ui/SampleChild'
class SampleParent extends Component {
constructor(props) {
super(props) ;
}
componentDidMount() {
const { dispatch } = this.props
dispatch(fetchNames());
}
render() {
return(<div><ul id="myUL">{this.props.reports.map((report) => (
<li>
<SampleChild
key={report.id}
label={report.label}
uri={() => fetchDownloadLink("http://localhost:8080/sample"+this.props.uri+".pdf")}
/>
</li>))}</ul></div>)}
}
function mapStateToProps(state) {
const { reports } = state
return {
reports
}
}
const mapDispatchToProps = dispatch => {
return {
fetchDownloadLink(url) {
dispatch(
fetchDownloadLink(url)
)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ReportsApp)
SampleChild.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { fetchDownloadLink } from '../../actions/actions'
class OpenReport extends Component {
constructor(props) {
super(props) ;
}
render(){
return(<div className="in_sample" id={this.props.label}>
{this.props.label}
<a href={this.props.uri}>
<img src="../images/pdf-file_128.png" height="25px" width="25px"></img></a><br></br></div>
)
}
}
module.exports = OpenReport;
Currently I am getting this error:
Uncaught TypeError: dispatch is not a function
at ReportsApp.componentDidMount (bundle.js:39883)
Basically what I need to do is get a url as a string from the 'fetchDownloadLink ' function and pass this string to my child component. Is there any other way to do that?
Please suggest...
Thanks in advance!
According to the Documentaion:
mapDispatchToProps returns an object that somehow uses dispatch to bind
action creators in your own way.
However in your case you are returning an object without keys. Change your function to
const mapDispatchToProps = dispatch => {
return {
fetchDownloadLink: (url) => dispatch(fetchDownloadLink(url))
}
}
}
MoreOver, using connect function you need to connect mapStateToProps and mapDispatchToProps function to the component in which you will be using the action creators which in your case is SampleParent
Also if you pass mapDispatchToProps as the second parameter to connect, then dispatch is not available as a prop to your component.
So change your code to the following
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { fetchNames, fetchDownloadLink } from '../../actions/actions'
import SampleChild from '../ui/SampleChild'
class SampleParent extends Component {
constructor(props) {
super(props) ;
}
componentDidMount() {
const { dispatch } = this.props
this.props.fetchNames();
}
render() {
return(<div><ul id="myUL">{this.props.reports.map((report) => (
<li>
<SampleChild
key={report.id}
label={report.label}
uri={() => this.props.fetchDownloadLink("http://localhost:8080/sample"+this.props.uri+".pdf")}
/>
</li>))}</ul></div>)}
}
}
function mapStateToProps(state) {
const { reports } = state
return {
reports
}
}
const mapDispatchToProps = dispatch => {
return {
fetchDownloadLink: (url) => dispatch(fetchDownloadLink(url)),
fetchNames: () => dispatch(fetchNames)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SampleParent)
The problem is that you are passing your action creator as a prop uri to your child component, and then using that prop as an HREF tag.
You should instead pass it as onChildClick (example name) prop to your report item component, and call it on the onClick prop of the <a>.
<a onClick={ this.props.onChildClick }>xxxx</a>
The previous answer about the action creator mapping is fine, but you don't even need a function: if you use an object with functions as keys, they will be wrapped with dispatch for you.
const mapDispatchToProps = {
fetchDownloadLink
}
EDIT after your updated question
I see the problem now. You don't have dispatch as prop because you're using mapDispatchToProps to provide some action creators as props. It doesn't make sense to map dispatch to fetchDownloadLink and not doing it as well for fetchNames. Map both or neither, but you shouldn't mix and match.

Resources