Jenny Mok
Jenny Mok

Reputation: 2804

redux async with redux thunk doesn't work with @connect

I try to use setTimeout as a mock for rest api, but my redux seems has flawed.

https://codesandbox.io/s/1zr78rp48j

partial code

@connect(state => state.items, { approveItem })
export default class Items extends Component {
  render() {
    return (
      <div>
        <div>status: {this.props.item.status}</div>
        <button onClick={() => approveItem()}>{this.props.loading ? 'loading...' : 'Approve'}</button>
      </div>
    );
  }
}

I wonder why this simple flow won't work, is my setTimeout function in the reducer make sense? I'm using redux-thunk.

Upvotes: 3

Views: 369

Answers (2)

HMR
HMR

Reputation: 39250

If you want redux to take the action creators and wrap them in a function that will dispatch their result you have to pass an object with the action creators as it's members to mapDispatchToProps (you are doing this correctly).

But in the component you are not using the wrapped action creator, you are using the imported approveItem The correct code to create auto dispatched action creators is:

import React, { Component } from "react";
import { connect } from "react-redux";
import { approveItem } from "./actions";
//mapDispatchToProps is an object here, all function members of the object
//  will be treated as action creators and wrapped in a function that when
//  called will automatically dispatch their result
const mapDispatchToProps = { approveItem };
@connect(state => state.items, mapDispatchToProps)
export default class Items extends Component {
  render() {
    //use the wrapped action creator found on this.props
    console.log(this.props.approveItem);
    return (
      <div>
        <div>status: {this.props.item.status}</div>
        <button onClick={() => this.props.approveItem()}>Approve </button>
      </div>
    );
  }
}

You can manually wrap action creators in a function that will dispatch their results (actions). By passing a function to mapDispatchToProps.

This is is usually the case when you want to isolate components and not dump all reducers and actions on one pile. The application will wrap actions in {type:"ITEM",action:actualItemComponentAction}. Since the component doesn't know how to wrap it's actions in a action handled by application the application needs to pass wrappers to action creators that return functions for thunk and wrap actual action objects with a type that can be handled by the application reducer.

Not sure how bindActionCreators fits into this because if you manually want to bind action creators to components you usually don't want to auto bind them but rather want to wrap the component action in a application action.

An example work in progress can be found here.

Upvotes: 0

Fadi Abo Msalam
Fadi Abo Msalam

Reputation: 7197

i have corrected your code , take a look

https://codesandbox.io/s/14j6m2661q

the issue was in your class

export  class Items extends Component {
  render() {
    console.log(approveItem);
    return (
      <div>
        <div>status: {this.props.items && this.props.items.item.status}</div>
        <button onClick={() => this.props.approveItem()}>Approve </button>
      </div>
    );
  }
}

// start of code change
const mapStateToProps = (state) => {
  return { items: state.items };
};
const mapDispatchToProps = (dispatch) => {

  return {
    approveItem: () => dispatch(approveItem())
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(Items);

Upvotes: 2

Related Questions