Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to make a REST API request from within a Redux reducer?

I have a React component named ItemList which loads a list of items from an API server and then renders them as a list of Item components.

Each Item has a delete button. When the button is clicked, I want to send a request to the API server to delete the item, then re-render the ItemList.

One way I can think of to make that work is to move both API queries into reducers, and then dispatch actions whenever I want to a) get all items; and b) delete an item. Upon successful completion of those API operations, the reducer will update the store and the ItemList will re-render.

Is that a reasonable approach, or is it a bad idea to put API calls inside of reducers?

Here's a simplified version of the the code I have so far. It doesn't yet use Redux. I want to make sure my approach is sound before implementing Redux, hence this Stack Overflow question.

ItemList.js

class ItemList extends Component {

  constructor(props) {
    super(props);
    this.state = {
      items: []
    };
    this.componentDidMount = this.componentDidMount.bind(this);
  }

  componentDidMount() {
    const url = 'https://api.example.com/api/v1.0/item';
    fetch(url, {
      method: "get"
    })
    .then(res => res.json())
    .then(response => {
      this.setState({items: response.data});
    });
  }

  render() {
    <div>
      {this.state.items.map((item, index) => (
        <Item key={item.id} item={item} />
      ))}
    </div>
  }
}

Item.js

class Item extends Component {

  deleteClicked() {
    /** 
     *  Is it ok to dispatch a "delete item" action 
     *  from here and then make the actual API call 
     *  in a reducer?
     */
  }

  render() {
    <div>
      <h2>{item.title}</h2>
      <a onClick={this.deleteClicked}>delete item</a>
    </div>
  }
}
like image 737
Derrick Miller Avatar asked Jun 08 '19 01:06

Derrick Miller


1 Answers

You're almost solved your task. To make you solution perfect use Action creators to make async calls and dispatch actions on completion. Reducer should be pure sync function.

For example ItemList component may use such action creator to extract items

const ExtractItemsAction = () => (dispatch) => {
    dispatch ({type: ITEMS_REQUESTED});
    const url = 'https://api.example.com/api/v1.0/item';
    fetch(url, {
        method: "get"
    })
    .then(res => res.json())
    .then(response => {
        dispatch({type: ITEMS_RECEIVED, items: response.data});
    });
}

And reducer will stay pure

function reducer (state = initalState, action)
{
    switch (action.type) {
        case ITEMS_REQUESTED:
            return { ...state, itemsRequested: true }
        case ITEMS_RECEIVED:
            return { ...state, itemsRequested: false, items: action.items }
        default
            return state;
    }
}

And don't forget to connect you component to Redux, and use Redux-thunk as middleware when creating store.

like image 95
Fyodor Avatar answered Sep 27 '22 22:09

Fyodor