Use async react-select with redux-saga
Asked Answered
F

2

11

I try to implement a async react-select (Select.Async). The problem is, we want to do the fetch in redux-saga. So if a user types something, the fetch-action should be triggered. Saga then fetches the record and saved them to the store. This works so far. Unfortunately loadOptions has to return a promise or the callback should be called. Since the newly retrieved options get propagated with a changing property, I see no way to use Select.Async together with saga to do the async fetch call. Any suggestions?

 <Select.Async
   multi={false}
   value={this.props.value}
   onChange={this.onChange}
   loadOptions={(searchTerm) =>  this.props.options.load(searchTerm)}
  />

I had a hack where i assigned the callback to a class variable and resolve it on componentWillReceiveProps. That way ugly and did not work properly so i look for a better solution.

Thanks

Fakery answered 28/3, 2017 at 14:52 Comment(0)
R
17

redux-saga is for handling side effects like asynchronously receiving options for react-select. That's why you should leave the async stuff to redux-saga. I have never used react-select but by just looking at the documentation I would solve it this way:

Your component gets very simple. Just get value and options from your redux store. optionsRequested is an action creator for the OPTIONS_REQUESTED action:

const ConnectedSelect = ({ value, options, optionsRequested }) => (
  <Select
    value={value}
    options={options}
    onInputChange={optionsRequested}
  />
)

export default connect(store => ({
  value: selectors.getValue(store),
  options: selectors.getOptions(store),
}), {
  optionsRequested: actions.optionsRequested,
})(ConnectedSelect)

A saga definition watches for OPTIONS_REQUESTED action that is trigged by onInputChange, loads the data with given searchTerm from server and dispatches OPTIONS_RECEIVED action to update redux store.

function* watchLoadOptions(searchTerm) {
  const options = yield call(api.getOptions, searchTerm)
  yield put(optionsReceived(options))
}

In other words: Make your Component as pure as possible and handle all side-effect/async calls in redux-saga

I hope this answer was useful for you.

Railing answered 2/4, 2017 at 13:29 Comment(10)
Thanks for your answer! Yes i'd like to solve the problem exactly like you described. The point is, the "onChange" (loadOptions) of react-select, where i have to call "optionsRequested", expects a promise to be returned. Thats because they expect you to call fetch there. As far as i know there is no way so resolve a promise in a components with saga?Fakery
Hi, I looked at the documentation here: github.com/JedWatson/react-select#usage and onChange doesn't have to return a promise. It's just an event handler. In my example I use onChange to call the optionsRequested Action Creator that is then dispatched by redux and ultimately handled by a saga that makes the service request.Railing
hey. oh sorry i see the confusion now. I dont want to use the value of the select as searchTerm in my sagas. React-select allows you to type to search. On this search i want to fetch new Options for my react-select for the user to select. This is because there can be thousands of records and i dont want to load them all initially. This can be achieved with loadOptions and there i need a promise.Fakery
Hi, I was also confused. Reading the docu again, instead of onChange I meant onInputChange. I updated my answer accordingly.Railing
Ok its a bit of an abuse of onInputChange. But if i use onInputChange and Select Component (Not Select.Async) it works with saga! Thanks for the answer!Fakery
I use your approach because i use redux saga. It is working fine but when i type the list is not updating with the values. When i close the list and the reopen it the list is fine. I use onInputKeyDownSarsen
@decay is your list updating successfully? My list is now updating even if the new values are in the stateSarsen
I am using VirtualizedSelect and redux (not redux saga). On my onInputChange I would like to call my API after typing the third character so my list is filtered. The event triggers and updated the state with the characters; However, after typing the second character the state and textbox value get cleaned. I am wondering if I am missing something or if there is something else triggering the onInputChange value. Sorry in advance if I am not asking my question in the correct thread. @Fakery do you have any example about you make it work with the event OnIputChange working calling the API.Woven
@johnwick0831 I solved the problem in the following thread #49018133Woven
I use react-select version 2.4.3. when new options come from the store it's not updated in Select. I added "filterOption={() => (true)}" to the Select component according to github.com/JedWatson/react-select/issues/…Affection
C
-1

The main idea is that you are capable to dispatch redux actions using application context from

import React from 'react';
import { connect } from 'react-redux';

import Select from '@components/Control/Form/Skin/Default/Select';
import { reduxGetter, reduxSetter, required as req } from '@helpers/form';
import { companyGetTrucksInit } from "@reduxActions/company";

import AppContext from '@app/AppContext';

const FIELD_NAME = 'truck';

export const getReduxValue = reduxGetter(FIELD_NAME);
export const setReduxValue = reduxSetter(FIELD_NAME);

const SelectCompanyTruck = (props) => {
    const {
        required,
        validate=[]
    } = props;

    const vRules = [...validate];

    if (required)
        vRules.push(req);


    return (
        <AppContext.Consumer>
            {({ dispatchAction }) => (
                <Select
                    loadOptions={(inputValue, callback) => {
                        function handleResponse(response) {
                            const { data: { items } } = response;
                            const options = items.map(i => ({ label: i.name, value: i.id }));

                            callback(options);
                        }

                        dispatchAction(companyGetTrucksInit, { resolve: handleResponse, inputValue });
                    }}
                    name={FIELD_NAME}
                    {...props}
                />
            )}
        </AppContext.Consumer>
    );
}

export default SelectCompanyTruck;
Cittern answered 13/3, 2019 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.