7

I want to fire an action down in my component. This is a presentation component and need not be redux aware. Router implementation is done using react-router-redux.

main.js:

let store = createStore(rootReducer)

const history = syncHistoryWithStore(hashHistory, store)

class RenderClass extends Component {
  render() {
    return (
        <Provider store={store}>
            <Router history={history}>
                <Route path="/" component={MainLayout}>
                    <Route name="employees" path="/employees" url="http://localhost:8080/" component={EmployeeTable}></Route>
                    <Route name="personalInformation" path="/personalInformation/:employeeId" url={URI} component={PersonalInformation} />
                    .....
                </Route>
            <Router>
        </Provider>
        );
    }
}   

App.jsx:

import * as Actions from './action-creator';
const MainLayout = React.createClass({
                render: function() {

                    const { dispatch, list } = this.props;
                    let actions = bindActionCreators(Actions, dispatch);
                    console.log(actions);

                    return (
                        <div className="wrapper">
                            <Header actions={actions} list={list} params={this.props.params} />

                    {React.cloneElement(this.props.children, this.props)}
                        </div>
                    )
                }
            }); 

function select(state) {
   return {
      list: state.listingReducer
   }
}
export default connect(select)(MainLayout);

header.js:

define(
    [
        'react',
        'jquery',
        'appMin'
    ],
    function (React, $) {
        var Link = require('reactRouter').Link;
        var Header = React.createClass({
            handleClick: function () {
                //Action called here
                this.props.actions.listEmployees();
            },      
            render: function () {
                return (
                    <ul className="sidebar-menu">
                        <li>
                            <Link to={'/employees'} onClick={this.handleClick}><i className="fa fa-home"></i>Employees</Link> 
                        </li> 
                    </ul>
                );
            }
        });
        return Header;
    }
)

employee.js:

define(
    [
        'react',
        'jquery',
        'appMin'
    ],
    function (React, $) {
        var EmployeeTable = React.createClass({

            render: function () {
                if (this.props.list != undefined) {
                    var listItems = this.props.list.map(function (listItem) {
                        return (
                            <tr key={listItem.id}>
                                <td> {listItem.name}</td>
                <td><Link to={'/personalInformation/' + employee.id} onClick={this.props.actions.displayPersonalInformation(employee.id)}>Personal Information</Link></td>
                                ......
                            </tr>
                        );
                    }, this);   
                    })
                }
                return (
                    <table>
                        <tbody>
                            {listItems}
                        </tbody>                    
                    </table>
                );
            }
        })  
    }
)

action-creator.js:

export function listEmployees() {
    return {
      type: types.LIST_EMPLOYEES
   };
}

export function displayPersonalInformation() {
        return {
          type: types.DISPLAY_PERSONAL_INFORMATION
       };
    }

reducer.js:
function addEmployee(state, action) {

   switch (action.type) {

      case types.LIST_EMPLOYEES:
         return {"id":"1", "name":"Stackoverflow"}

      default:
        return state
   }
}

function listingReducer(state = [], action) {
    switch (action.type) {

        case types.LIST_EMPLOYEES:
            return [
                ...state, 
                addEmployee(undefined, action)
            ]

        case types.DISPLAY_PERSONAL_INFORMATION:
                return // Gets personal Information

        default:
            return state;
    }
}


const rootReducer = combineReducers({
   listingReducer
})

export default rootReducer

The props will not contain actions since I have not tied it to props. I tried writing mapStateToProps and mapDispatchToProps in App.js as shown below:

function mapStateToProps(state) {
   return {
        list: state.list
    }
}

function mapDispatchToProps(dispatch) {
   return { actions: bindActionCreators({ actionCreators }, dispatch) }
}

export default connect(mapStateToProps, mapDispatchToProps)(MainLayout) I'm getting dispatch is not a function error. The error statement actually sounds like a duplicate question in stackoverflow, but I would also like to know the purpose why I'm using mapDispatchToProps is also right. Thanks in advance.

Brandon Søren Culley
  • 3,801
  • 1
  • 25
  • 27
User1230321
  • 1,165
  • 4
  • 17
  • 36
  • On which part od code your getting this error? – Thaadikkaaran Nov 22 '16 at 07:11
  • @JaganathanBantheswaran: I'm getting this error in mapDispatchToProps function and after the action is fired. More than the error, I would like to know what am I supposed to do when I'm supposed to use an action in presentation component when redux router is used. – User1230321 Nov 22 '16 at 07:37

5 Answers5

5

So from what I understand you are using export default connect(mapStateToProps, mapDispatchToProps)(MainLayout) and trying to call dispatch in MainLayout.

So from the react-redux documentation if you see you can call connect in following ways:-

  • connect()(//Component) --> dispatch would be passed as props
  • connect(mapStateToProps)(//Component) --> dispatch would be passed as props
  • connect(mapStateToProps,mapDispatchToProps)(//Component) --> dispatch would not be passed as props to your component

In scenario 3 the behavior is this way because mapDispatchToProps already has access to dispatch function. So you can bind your function to dispatch in mapDispatchToProps and then pass on this function to your component.

Example

The function you want to dispatch are dispatchMe() and dispatchMe2() which calls an action called dispatchAction. So your mapDispatchToProps would be something like :-

function mapDispatchToProps(dispatch){
  return {
     dispatchMe : () => {
       dispatch(dispatchAction()) 
    },
    dispatchMe2 : () => {
      dispatch(dispatchAction2())
    }
  }
}

Now in your component you can pass dispatchMe and call it when required.

You can read here for more info on this

Harkirat Saluja
  • 6,827
  • 4
  • 39
  • 65
  • Thanks for your reply. Basically I'd to use action "displayPersonalInformation" in my Employee component which is a presentation component and hence not redux aware. For which you have suggested me to put the entry of it in mapDispatchToProps(as shown by you) and have access to it. Now I'm trying to make it generic. If I want to use any action in the presentation component, then I have to have that action entry in my mapDispatchToProps. Please correct me if I'm wrong. – User1230321 Nov 22 '16 at 07:19
  • Yeah that should do. Ideally you should create one `EmployeeContainer` and pass `mapDispatchToProps` to your `Employee` component from your container as props. – Harkirat Saluja Nov 22 '16 at 07:37
  • How will the mapDispatchToProps look then? Because I will have a set of action creators that might be used in that component. Will each action creator have a separate entry in the mapDispatchToProps? And summarizing again, "bindActionCreators" will never be used in mapDispatchToProps. – User1230321 Nov 22 '16 at 07:47
  • @Harikirat, I'm still getting the error "display is not a function" in bindActionCreators.js file after putting mapDispatchToProps in my connect function. – User1230321 Nov 22 '16 at 08:13
  • Can you share your code once? I am updating answer for multiple dispatch actions – Harkirat Saluja Nov 22 '16 at 08:39
  • Will share the code soon. But look at this example of how they have used bindActionCreators in mapDispatchToProps. https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options – User1230321 Nov 22 '16 at 10:09
0

Since you have used bindActionCreators, redux would dispatch the action whenever you call the action creator i.e displayPersonalInformation in your case.

As @Harkirat Saluja mentioned in his answer, if you have used mapDispatchToProps then your component would not have the dispatch function bound with props.

But still if you want to have dispatch with props (not best practice), then you can have something like,

function mapDispatchToProps(dispatch) {
   return { dispatch, ...actions: bindActionCreators({ actionCreators }, dispatch) }
}
Thaadikkaaran
  • 4,648
  • 7
  • 31
  • 54
  • thanks for the reply. Can I not tie my actions to props at all in redux. Because that was my core problem. Pardon me if I'm sounding silly. – User1230321 Nov 22 '16 at 07:51
  • I'm still getting the error "display is not a function" in bindActionCreators.js file after putting mapDispatchToProps in my connect function as Harikirat said. – User1230321 Nov 22 '16 at 08:18
  • I am not able to get why would you want bind dispatch props when you already have it in mapDispatchToProps. – Harkirat Saluja Nov 22 '16 at 08:41
  • @HarkiratSaluja if you want to have `dispatch` function with `props` (not best practice), then you can have it as i shown. Read my answer with bit more care – Thaadikkaaran Nov 22 '16 at 08:52
  • @Harkirat: Right, I don't need it. – User1230321 Nov 22 '16 at 09:59
0
  class MainLayout extends React.component{
      render(){
        console.log(this.props.list);
        return(
            <div>
               <h1>mainlayout</h1>
             </div>
            );
        } 
    }
  function mapStateToProps(state) {
       return {
           list: state.listingReducer
       }
   }
   export default connect(mapStateToProps)(MainLayout);
SM Chinna
  • 291
  • 2
  • 7
0

You cannot access your actions through props because you are assigning your actions to the action attribute. The way you bind your actions to your props you could access your functions through this.props.actions.myFunction. If you want to call your actions directly through props connect your actions to your props like this:

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators(actionCreators, dispatch);
};

export default connect(null, mapDispatchToProps)(MainLayout);
Roumelis George
  • 5,870
  • 2
  • 14
  • 29
0
import { bindActionCreators, } from 'redux';
import { connect, } from 'react-redux';

/**
 * Map redux actions to the Component's props
 * @param {*} dispatch The Redux store's dispatch method
 * @returns {*} Object
 */
const mapDispatchToProps = (dispatch) => {
    return bindActionCreators({
        yourActions,
    }, dispatch);
};

export default connect(mapDispatchToProps)(MainLayout);