首页 > 解决方案 > 每次输入更改时如何使用服务器数据制作 antd 自动完成更新下拉列表

问题描述

我有一些使用 antd、redux、saga 的项目,特别是我使用 antd 自动完成https://ant.design/components/auto-complete/

我试图让 antd 自动完成与服务器数据一起工作,并在每次用户在输入中输入内容时发送 redux 操作。问题是我不知道如何使它与 redux saga 一起工作。

假设我成功地从服务器获取数据并使用该数据更新存储。那么如何在下拉列表中显示这些数据。每次用户类型时,下拉数据可能会更改

const {createStore, applyMiddleware} = Redux;
const {Provider, connect} = ReactRedux;
const { createAction, handleActions } = window.ReduxActions;
const createSagaMiddleware = ReduxSaga.default;
const {takeEvery, takeLatest, put, call} = ReduxSaga.effects;
const { AutoComplete } = antd;
const { Option, OptGroup } = AutoComplete;




const initialState = {
	isLoading: false,
	list: [],
};


var dataSource = [
  {
    "directionId": "3e8a6a23-d325-4c74-9bb5-e83c0bd4de4e",
    "directionName": "ABC",
    "items": [
      {
        "docflowType": 71,
        "docflowTypeName": "infoABC",
        "startDate": "2019-10-07T09:47:32.119004",
        "endDate": "2019-10-07T09:47:32.119004",
        "count": 1
      }
    ]
  },
  {
    "directionId": "7feb83e0-4d7b-4d99-8adb-23c25473d57d",
    "directionName": "TGK",
    "items": [
      {
        "docflowType": 74,
        "docflowTypeName": "reportG",
        "startDate": "2019-10-07T09:42:08.549327",
        "endDate": "2019-10-07T09:42:08.549327",
        "count": 1
      }
    ]
  }
]


function renderTitle(title) {
  return (
    <span>{title}</span>
  );
}

const options = dataSource.map(group => (
    <OptGroup key={group.directionId} label={renderTitle(group.directionName)}>
      {group.items.map(opt => (
        <Option key={opt.docflowTypeName} value={opt.docflowTypeName}>
          {opt.docflowTypeName}
          <span className="certain-search-item-count"> {opt.count} организаций</span>
        </Option>
      ))}
    </OptGroup>
));


const fetchSuggest = createAction('FETCH_SUGGEST_REQUEST', payload => payload);
const fetchSuggestSuccess = createAction('FETCH_SUGGEST_SUCCESS', result => result);
const fetchSuggestError = createAction('FETCH_SUGGEST_ERROR');

const autoSuggesting = handleActions(
	{
		[fetchSuggest]: (state, action) => ({
		  ...state,
		  isLoading: true,
		}),
		[fetchSuggestSuccess]: (state, action) => { 
			console.log("fetchSuggestSuccess", action);
		return {
		  ...state,
		  isLoading: false,
		  list: action.payload,
		}},
		[fetchSuggestError]: (state, action) => ({
		  ...state,
		  isLoading: false,
		})
	},
	initialState
);

const fetchList = async payload => {
	var {filter, exclusion} = payload || {};
  	const response = await axios.get(`http://someserver.com?filter=${filter}&exceptTestCodes=${exclusion}`);
	return response.data;
};

function* fetchSuggestion(action) {
console.log("worker saga получает", action.payload)
  try {
    const data = action.payload;
    const response = yield call(fetchList, data);
	console.log(response);
    // yield delay(200);
    yield put(fetchSuggestSuccess(response));
  } catch (error) {
    yield put(fetchSuggestError());
  }
}


function* watchInput() {
	yield takeLatest('FETCH_SUGGEST_REQUEST', fetchSuggestion);
}

const sagaMiddleware = createSagaMiddleware();
const store = createStore(autoSuggesting, initialState, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(watchInput);

class App extends React.Component {
	constructor(props) {
		super(props);
		this.state = { value: '', dataSource:[] };
	}
	
	handleSearch = searchText => {
		this.setState({
		  dataSource: !searchText ? [] : this.getListToDisplay(),
		});
	}
	
	handleChange = value => {
		this.setState({ value });
		this.props.fetchSuggest({filter:value, exclusion: false});
    }
	
	getListToDisplay = () => {
		return this.props.list.map(group => (
			<OptGroup key={group.directionId} label={renderTitle(group.directionName)}>
			  {group.items.map(opt => (
				<Option key={opt.docflowTypeName} value={opt.docflowTypeName}>
				  {opt.docflowTypeName}
				  <span className="certain-search-item-count"> {opt.count} организаций</span>
				</Option>
			  ))}
			</OptGroup>
		))
	}
	
	render() {
		return (
			<React.Fragment>
			<AutoComplete
				value={this.state.value}
				onChange={this.handleChange}
				onSearch={this.handleSearch}
				onSearch={this.handleSearch}
				dataSource={this.getListToDisplay()}
				optionLabelProp="value"
				dropdownMatchSelectWidth={false}
				dropdownStyle={{ width: 405 }}
				style={{ width: '30%', overflow: 'hidden' }}
				// filterOption
			>
			</AutoComplete>
			</React.Fragment>
		);
	}
}

const mapStateToProps = state => state;
const mapDispatchToProps = dispatch => ({ fetchSuggest: (data) => dispatch(fetchSuggest(data)) });
const ConnectedApp = connect(mapStateToProps, mapDispatchToProps)(App);

ReactDOM.render(<Provider store={store}><ConnectedApp/></Provider>, document.getElementById('root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.0.3/react-redux.js" crossorigin="anonymous"></script>
	<script src="https://unpkg.com/redux@latest/dist/redux.js"></script>
    <script src="https://unpkg.com/redux-actions@latest/dist/redux-actions.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-saga/1.0.2/redux-saga.umd.js" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/babel-standalone@latest/babel.min.js" crossorigin="anonymous"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.4.4/polyfill.min.js" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
	<script src="https://unpkg.com/moment/min/moment-with-locales.js"></script>
    <script src="https://unpkg.com/antd/dist/antd-with-locales.js"></script>
	<link rel="stylesheet" href="https://unpkg.com/antd/dist/antd.css" />

标签: javascriptreactjsantd

解决方案


这是在基于 ajax 的下拉列表的官方文档中给出的

import { Select, Spin } from 'antd';
import debounce from 'lodash/debounce';

function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) {
  const [fetching, setFetching] = React.useState(false);
  const [options, setOptions] = React.useState([]);
  const fetchRef = React.useRef(0);
  const debounceFetcher = React.useMemo(() => {
    const loadOptions = (value) => {
      fetchRef.current += 1;
      const fetchId = fetchRef.current;
      setOptions([]);
      setFetching(true);
      fetchOptions(value).then((newOptions) => {
        if (fetchId !== fetchRef.current) {
          // for fetch callback order
          return;
        }

        setOptions(newOptions);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
  }, [fetchOptions, debounceTimeout]);
  return (
    <Select
      labelInValue
      filterOption={false}
      onSearch={debounceFetcher}
      notFoundContent={fetching ? <Spin size="small" /> : null}
      {...props}
      options={options}
    />
  );
} // Usage of DebounceSelect

async function fetchUserList(username) {
  console.log('fetching user', username);
  return fetch('https://randomuser.me/api/?results=5')
    .then((response) => response.json())
    .then((body) =>
      body.results.map((user) => ({
        label: `${user.name.first} ${user.name.last}`,
        value: user.login.username,
      })),
    );
}

const Demo = () => {
  const [value, setValue] = React.useState([]);
  return (
    <DebounceSelect
      mode="multiple"
      value={value}
      placeholder="Select users"
      fetchOptions={fetchUserList}
      onChange={(newValue) => {
        setValue(newValue);
      }}
      style={{
        width: '100%',
      }}
    />
  );
};

ReactDOM.render(<Demo />, mountNode);

推荐阅读