Redux thunk - how to prevent it from making unnecessary API request?

≯℡__Kan透↙ 提交于 2021-02-07 17:26:35

问题


I have action.js file like this:

import axios from 'axios';

export const SEARCH_TERM = 'SEARCH_TERM';
export const SAVE_SEARCH = 'SAVE_SEARCH';

export function search(query) {
  const githubApi = `https://api.github.com/search/repositories?q=${query}&sort=stars&order=desc`;
  const request = axios.get(githubApi);
  return (dispatch) => {
    request.then(({ data: dataFromGithub }) => {
      dispatch({ type: SEARCH_TERM, payload: query });
      dispatch({ type: SAVE_SEARCH, payloadOne: query, payloadTwo: dataFromGithub });
    });
  };
}

Using reducers I'm saving to redux store all search terms that user inputs. Then I fire a request to github api and save response data as well.

Now I have a problem, and I really don't know yet how to deal with it.

How can I write code that checks if the user searched already for this query before, and in that case, my app won't fire that request to github api.

How can I do this and where I should put this logic? Any ideas?


EDIT: Thanks to @klugjo! Because of his hint I wrote code that actually did work.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import _ from 'lodash';
import logo from './logo.svg';
import './style.css';

import SearchBar from '../SearchBar';

import { search } from '../../actions/';

class App extends Component {
  startSearch(query) {
    const storedTerms = this.props.storedSearchTerms;
    let foundDuplicate = false;

    if (storedTerms.length === 0) {
      return this.props.search(query);
    }

    if (storedTerms.length !== 0) {
      const testDuplicate = storedTerms.map(term => term === query);
      foundDuplicate = testDuplicate.some(element => element);
    }

    if (foundDuplicate) {
      return false;
    }

    return this.props.search(query);
  }

  render() {
    const searchDebounced = _.debounce(query => this.startSearch(query), 2000);
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started...
        </p>
        <SearchBar handleSearchQueryChange={searchDebounced} />
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    storedSearchTerms: state.searchTerm,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ search }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(App);

回答1:


You will have to do the check from inside your React component.

Based on your code, I would say that you are saving the list of queries that were executed:

dispatch({ type: SEARCH_TERM, payload: query });

In your .jsx container, only execute the search action if that query does not exist in your list of past queries.

In my opinion, passing or accessing your redux state from within the action creator is a an anti pattern. You can read more on that here.




回答2:


According to redux-thunk-docs, right in the >>Motivation<< part,

The thunk can be used [...] to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.

Now, this seems to me to be taken pretty straightforward ...

Also, I'd definitely not trigger the AJAX-Method before passing on control flow via returning the function that handles ... the active Async-Promise. I'd just push the ajax call inside the returning function, which is necessary to make it conditional anyway :)

export function search(query) {
  const githubApi = `https://api.github.com/search/repositories?q=${query}&sort=stars&order=desc`;
  const request = axios.get.bind(axios, githubApi);
  return (dispatch, getStore) => {
    const { SaveSearchReducer } = getStore();
    // consider writing a proper regex or something that fits your need
    const redundantPayload = SaveSearchReducer.filter(payload => !!payload.payloadOne.match(query));

    redundantPayload.length === 0 ?
      request().then(({ data: dataFromGithub }) => {
        dispatch({ type: SEARCH_TERM, payload: query });
        dispatch({ type: SAVE_SEARCH, payloadOne: query, payloadTwo: dataFromGithub });
      }) : 
      (() => {
        dispatch({ type: SEARCH_TERM, payload: query });
        dispatch({ type: SAVE_SEARCH, payloadOne: query, payloadTwo: redundantPayload[0].payloadTwo });
        // consider doing something more intelligent above here
        // you might have search terms that are contained by other search terms or similar and therefor might end up with multiple fitting previous searches/payloads
      })();
  };
}

OK, I haven't tested this, so there might be bugs or errors, but I think it should help you find your solution. Cheers, J



来源:https://stackoverflow.com/questions/47973117/redux-thunk-how-to-prevent-it-from-making-unnecessary-api-request

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!