Another way to work with Promise for Redux

    It turned out that at the moment I am taking part in the development of a front-end application (React + Redux) that makes a lot of requests to the REST API every minute, if not second.


    I’m tired of writing REQUEST / FAILURE / SUCCESS (hereinafter RFS) actions for every request, cases for them for the reducer, all this is plentifully watered with tests (quality is above all else).


    I wrote another bike.


    Jet bike


    Existing problem


    A lot of libraries have already been written for convenient work with RFS, but they involve a detailed setup of each of the three actions + writing cases for the reducer + tests. Basically, these libraries can be used in 100% of cases when writing requests to the server. Such flexibility requires writing a ton of code for similar tasks.


    In the project that I am currently working on, 90% of requests perform an extremely simple task: go to the server, take the data, convert it a bit, put it in state , if something went wrong - complain. Thousands of test lines have been written, which are inherently copy-paste with micro-changes.


    To the point


    How long? Weekends in front of the monitor, a liter of coffee, a shawarma for lunch and dinner, a little controlled magic ...


    don't open it

    whack


    ... the library is ready.


    The fromTo function (from, to, [through]) will take the data where it is said, convert it as it should, and put it in the specified location (any in your state , but only if you use the immutable library for the reducer ). The reducer will independently (after wrapping it in fromTo.wrapper) understand how to work with data and predictably change state in accordance with RFS actions (which the library will take care of itself). Everything is covered with tests (if you find bugs, push mercilessly or open a ticket ).


    Feature Summary


    The simplest way (this is about half the time on our project) using fromTo action is:


    // запрос к серверу возвращает {"mood": "happy"} , HTTP200
    dispatch(fromTo(
      () => axios.get('dogs.io/good-boy'),
      ['dogs', 'goodBoy'],
    ));

    Here is what will happen in your state at this time :


    Initial state


    {
      ...otherReducers,
      dogs: Immutable.fromJS({}),
    }

    REQUEST


    {
      ...otherReducers,
      dogs: Immutable.fromJS({
        goodBoy: {
          isRequesting: true,
        },
      }),
    }

    SUCCESS


    {
      ...otherReducers,
      dogs: Immutable.fromJS({
        goodBoy: {
          isRequesting: false,
          data: {
            mood: 'happy',
          },
        },
      }),
    }

    For toanother 40% of cases, the object is used as an argument and a third (optional) argument is added. In eastern cases, we act in the old fashioned way ( fromTonot a panacea, but a convenient tool).


    Function parsing fromTo(from, to, [through])


    The function has 3 arguments.


    1. from. We say where to get the data. Function that returns Promise. Will be called without arguments.


    2. to. We say where to save the data.


      • If you use the object as an argument, then you need 3 keys: { request, failure, success }. These are 3 coordinates for the data in your state , where they will be saved::
        requestcoordinates for the Boolean value whether the call was completed from(for example [ 'dogs', 'isFetching', 1 ]) ,:
        failurecoordinates for the data that returned when reject(for example [ 'cats', 'errors', 2 ])
        success,: coordinates for the data that returned when resolve(for example [ 'robots', 'data', 3 ]).
      • You can restrict yourself to a list (for example [ 'goodBoy' ]), then this argument will be converted to an object{ request: [ 'goodBoy', 'is Requesting' ], failure: [ 'goodBoy', 'error' ], success: [ 'goodBoy', 'data' ] }

    3. [through]. We are talking (and we can not even talk and trust in defaults) how to convert the data that we returned to the investigation from(). An object can contain one or both of the methods:
      • requestAdapter- a function that will receive input from resolve. The returned data will be saved with SUCCESS,
      • errorAdapter- a function that will receive input from reject. The returned data will be saved with FAILURE.

    Total


    At the moment, on the project, we save hours of our time and bundles of nerves due to the lack of the need to write tons of monotonous code. Instead of 90% of actions, reducers, tests, one function is called dispatch(fromTo(...args)). We are glad.


    Of course, a certain number of tests are still written, but basically these are tests for the argument through(required) and a tribute to TDD.


    In the comments I would like to see the opinion of colleagues in the workshop, constructive criticism is more than welcome. Raw in a gita , library in an enpeem , a duck in a hare, a hare in a chest, and he is on a tree.


    Have a nice day.


    Also popular now: