React-Select Async loadOptions не загружает параметры должным образом - PullRequest
0 голосов
/ 25 октября 2018

React Async. Выберите loadoption, иногда не удается загрузить опцию.Это очень странное явление после того, как пара наборов запросов реагирует loadoptions не загружают никакие значения, но я вижу из журнала, что результаты правильно получены из внутреннего запроса.Моя кодовая база полностью обновлена ​​с выпуском реагировать-выбрать новый выпуск и использовать

«реагировать-выбор": "^ 2.1.1"

Вот мой интерфейскод для реакции-асинхронного выбора компонента.Я использую debounce в своей функции getOptions, чтобы уменьшить количество поисковых запросов бэкэнда.Это не должно вызвать никаких проблем, я думаю.Я хотел бы добавить еще один момент, который я наблюдаю в этом случае, индикатор загрузки загрузки (...) также не появляется в этом явлении.

import React from 'react';
import AsyncSelect from 'react-select/lib/Async';
import Typography from '@material-ui/core/Typography';
import i18n from 'react-intl-universal';

const _ = require('lodash');

class SearchableSelect extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      searchApiUrl: props.searchApiUrl,
      limit: props.limit,
      selectedOption: this.props.defaultValue
    };
    this.getOptions = _.debounce(this.getOptions.bind(this), 500);
    //this.getOptions = this.getOptions.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.noOptionsMessage = this.noOptionsMessage.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleChange(selectedOption) {
    this.setState({
      selectedOption: selectedOption
    });
    if (this.props.actionOnSelectedOption) {
      // this is for update action on selectedOption
      this.props.actionOnSelectedOption(selectedOption.value);
    }
  }

  handleInputChange(inputValue) {
    this.setState({ inputValue });
    return inputValue;
  }

  async getOptions(inputValue, callback) {
    console.log('in getOptions'); // never print
    if (!inputValue) {
      return callback([]);
    }
    const response = await fetch(
      `${this.state.searchApiUrl}?search=${inputValue}&limit=${
        this.state.limit
      }`
    );
    const json = await response.json();
    console.log('results', json.results); // never print
    return callback(json.results);
  }

  noOptionsMessage(props) {
    if (this.state.inputValue === '') {
      return (
        <Typography {...props.innerProps} align="center" variant="title">
          {i18n.get('app.commons.label.search')}
        </Typography>
      );
    }
    return (
      <Typography {...props.innerProps} align="center" variant="title">
        {i18n.get('app.commons.errors.emptySearchResult')}
      </Typography>
    );
  }
  getOptionValue = option => {
    return option.value || option.id;
  };

  getOptionLabel = option => {
    return option.label || option.name;
  };

  render() {
    const { defaultOptions, placeholder } = this.props;
    return (
      <AsyncSelect
        cacheOptions
        value={this.state.selectedOption}
        noOptionsMessage={this.noOptionsMessage}
        getOptionValue={this.getOptionValue}
        getOptionLabel={this.getOptionLabel}
        defaultOptions={defaultOptions}
        loadOptions={this.getOptions}
        placeholder={placeholder}
        onChange={this.handleChange}
      />
    );
  }
}

export default SearchableSelect;

Изменить до ответа Ответ Стива

Спасибо за ваш ответ, Стив.Все еще не повезло.Я пытаюсь ответить в соответствии с вашими точками ответа.

  1. Если я не использую optionsValue, лучше использовать getOptionValue и getOptionLevel, тогда результат запроса не загружается должным образом.Я имею в виду там пустые параметры загружены, без текстового значения.
  2. да, вы правы, это синхронный метод, возвращающий строку, мне не нужно переопределять это.И это работает нормально, и noOptionsMessage показывает правильно.Спасибо, чтобы указать на это.
  3. actionOnSelectedOption не является noop-методом, он может нести определенную ответственность за выполнение.Я пытаюсь использовать SearchableSelect в качестве независимого компонента, если мне нужно какое-то внутреннее действие, чтобы эта функция инициировала это соответственно.Например, я использую это в профиле пользователя моего проекта, где пользователь может обновить информацию о своей школе / колледже из существующих записей.Когда пользователь выбирает параметр, ответственность за обновление профиля лежит на нем.
  4. Да, вы правы.Мне не нужно поддерживать inputValue в состоянии, спасибо.
  5. Я уверен, что defaultOptions - это массив.
  6. Я тестирую без использования debounce, но все равно не повезло.я использую debounce, чтобы ограничить бэкэнд-вызов, иначе может быть бэкэнд-вызов для каждого нажатия клавиши, которое, конечно, мне не нужно.

асинхронный выбор отлично работает для 2/3 запросов, после чего он внезапно перестает работать.Одно различимое поведение я наблюдаю, что для этих случаев поисковые индикаторы (...) также не отображаются.

Большое вам спасибо за ваше время.

Редактировать 2 в ответе Ответ Стива

Большое спасибо за ваш ответ еще раз.Я был не прав насчет getOptionValue и getOptionLabel.Если loadOptions получил ответ, то обе эти функции вызваны.Поэтому я удалил свою вспомогательную функцию optionsValue из моего предыдущего фрагмента кода и обновил свой фрагмент кода в соответствии с (В этом посте также).Но все равно не повезло.В некоторых случаях async-select не работал.Я пытаюсь сделать скриншот одного из таких случаев.Я использую имя в названии local-db "tamim johnson", но когда я искал его, я не получил никакого ответа, но получил правильный ответ от back-end.Вот скриншот этого случая tamim johnson

Я не уверен, насколько ясен этот скриншот.Тамим Джонсон также на 6-й позиции в моем рейтинге.

Спасибо, сэр, за ваше время.Я понятия не имею, что я делаю неправильно или что-то упускаю.

Редактировать 3 в ответе ответа Стива

Это ответ на вкладке предварительного просмотра для поиска пользователя с именем "tamim johnson".

preview tab

Ответы [ 3 ]

0 голосов
/ 28 декабря 2018

Проблема в том, что функция отладки Lodash не подходит для этого.Lodash указывает, что

последующие вызовы дебазированной функции возвращают результат последнего вызова функции

Не так:

возврат последующих вызововобещания, которые будут преобразованы в результат следующего вызова func

Это означает, что каждый вызов, находящийся в пределах периода ожидания для функции debounce loadOptions prop, фактически возвращает последний вызов func, и поэтому"Обещание, о котором мы заботимся, никогда не подписывается.

Вместо этого используйте функцию отката с обещанием возврата

Например:

import debounce from "debounce-promise";

//...
this.getOptions = debounce(this.getOptions.bind(this), 500);

См. полный текстобъяснение https://github.com/JedWatson/react-select/issues/3075#issuecomment-450194917

0 голосов
/ 12 февраля 2019

Я узнал, что люди намерены искать эту проблему.Так что я публикую свою часть обновления кода, которая решает проблему.Преобразование из асинхронного ожидания в обычную функцию обратного вызова исправило мою проблему.Отдельное спасибо Стиву и другим.

import React from 'react';
import AsyncSelect from 'react-select/lib/Async';
import { loadingMessage, noOptionsMessage } from './utils';
import _ from 'lodash';

class SearchableSelect extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      selectedOption: this.props.defaultValue
    };
    this.getOptions = _.debounce(this.getOptions.bind(this), 500);
  }

  handleChange = selectedOption => {
    this.setState({
      selectedOption: selectedOption
    });
    if (this.props.actionOnSelectedOption) {
      this.props.actionOnSelectedOption(selectedOption.value);
    }
  };

  mapOptionsToValues = options => {
    return options.map(option => ({
      value: option.id,
      label: option.name
    }));
  };

  getOptions = (inputValue, callback) => {
    if (!inputValue) {
      return callback([]);
    }

    const { searchApiUrl } = this.props;
    const limit =
      this.props.limit || process.env['REACT_APP_DROPDOWN_ITEMS_LIMIT'] || 5;
    const queryAdder = searchApiUrl.indexOf('?') === -1 ? '?' : '&';
    const fetchURL = `${searchApiUrl}${queryAdder}search=${inputValue}&limit=${limit}`;

    fetch(fetchURL).then(response => {
      response.json().then(data => {
        const results = data.results;
        if (this.props.mapOptionsToValues)
          callback(this.props.mapOptionsToValues(results));
        else callback(this.mapOptionsToValues(results));
      });
    });
  };

  render() {
    const { defaultOptions, placeholder, inputId } = this.props;
    return (
      <AsyncSelect
        inputId={inputId}
        cacheOptions
        value={this.state.selectedOption}
        defaultOptions={defaultOptions}
        loadOptions={this.getOptions}
        placeholder={placeholder}
        onChange={this.handleChange}
        noOptionsMessage={noOptionsMessage}
        loadingMessage={loadingMessage}
      />
    );
  }
}

export default SearchableSelect;
0 голосов
/ 25 октября 2018

Некоторые примечания можно найти под кодом.Вы ищете что-то вроде этого:

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import AsyncSelect from 'react-select/lib/Async';
import debounce from 'lodash.debounce';
import noop from 'lodash.noop';
import i18n from 'myinternationalization';

const propTypes = {
  searchApiUrl: PropTypes.string.isRequired,
  limit: PropTypes.number,
  defaultValue: PropTypes.object,
  actionOnSelectedOption: PropTypes.func
};

const defaultProps = {
  limit: 25,
  defaultValue: null,
  actionOnSelectedOption: noop
};

export default class SearchableSelect extends Component {
  static propTypes = propTypes;
  static defaultProps = defaultProps;
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      searchApiUrl: props.searchApiUrl,
      limit: props.limit,
      selectedOption: this.props.defaultValue,
      actionOnSelectedOption: props.actionOnSelectedOption
    };
    this.getOptions = debounce(this.getOptions.bind(this), 500);
    this.handleChange = this.handleChange.bind(this);
    this.noOptionsMessage = this.noOptionsMessage.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
  }

  getOptionValue = (option) => option.id;

  getOptionLabel = (option) => option.name;

  handleChange(selectedOption) {
    this.setState({
      selectedOption: selectedOption
    });
    // this is for update action on selectedOption
    this.state.actionOnSelectedOption(selectedOption.value);
  }

  async getOptions(inputValue) {
    if (!inputValue) {
      return [];
    }
    const response = await fetch(
      `${this.state.searchApiUrl}?search=${inputValue}&limit=${
      this.state.limit
      }`
    );
    const json = await response.json();
    return json.results;
  }

  handleInputChange(inputValue) {
    this.setState({ inputValue });
    return inputValue;
  }

  noOptionsMessage(inputValue) {
    if (this.props.options.length) return null;
    if (!inputValue) {
      return i18n.get('app.commons.label.search');
    }

    return i18n.get('app.commons.errors.emptySearchResult');
  }

  render() {
    const { defaultOptions, placeholder } = this.props;
    const { selectedOption } = this.state;
    return (
      <AsyncSelect
        cacheOptions
        value={selectedOption}
        noOptionsMessage={this.noOptionsMessage}
        getOptionValue={this.getOptionValue}
        getOptionLabel={this.getOptionLabel}
        defaultOptions={defaultOptions}
        loadOptions={this.getOptions}
        placeholder={placeholder}
        onChange={this.handleChange}
      />
    );
  }
}
  1. Вам не нужен метод для сопоставления набора результатов.Есть реквизиты, которые справятся с этим для вас.
  2. Если ваш i18n.get() является синхронным методом, возвращающим строку, вам не нужно переопределять весь компонент (даже для изменений стиля)
  3. Если вы по умолчанию используете actionOnSelectedOption дляnoop, тогда вам больше не требуется условное выражение для его вызова.
  4. React-Select отслеживает inputValue внутренне.Если у вас нет внешней потребности (вашей оболочки), нет необходимости пытаться управлять его состоянием.
  5. defaultOptions или
    • массив параметров по умолчанию (не будет вызыватьloadOptions до фильтрации)
    • true (будет автоматически загружаться из вашего loadOptions метода)
  6. Асинхронные / ожидающие функции возвращают обещание, используя ответ на обещаниеа не тип callback.

Мне интересно, если, обернув ваш метод getOptions() в debounce, вы нарушаете область действия this с вашим компонентом.Точно сказать не могу, поскольку я никогда раньше не использовал debounce.Вы можете взять эту оболочку и попробовать свой код для тестирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...