2

I would like to use the awesome react-widgets DropDownList to load records on demand from the server.

My data load all seems to be working. But when the data prop changes, the DropDownList component is not displaying items, I get a message

The filter returned no results

enter image description here

Even though I see the data is populated in my component in the useEffect hook logging the data.length below.

enter image description here

I think this may be due to the "filter" prop doing some kind of client side filtering, but enabling this is how I get an input control to enter the search term and it does fire "onSearch"

Also, if I use my own component for display with props valueComponent or listComponent it bombs I believe when the list is initially empty.

What am I doing wrong? Can I use react-widgets DropDownList to load data on demand in this manner?

//const ItemComponent = ({item}) => <span>{item.id}: {item.name}</span>;

const DropDownUi = ({data, searching, fetchData}) => {

const onSearch = (search) => {
  fetchData(search);
}

// I can see the data coming back here!
useEffect(() => { 
  console.log(data.length); 
 }, [data]); 


<DropDownList
 data={data}
 filter
 valueField={id}
 textField={name}
 onSearch={onSearch}
 busy={searching} />
};
user210757
  • 6,996
  • 17
  • 66
  • 115

2 Answers2

0

So it i think that list should be loaded a then you can filtering your loaded data.In your example on the beginning you don't have value so list is empty, you tape in some text and then value of list re render but it look like is not filtered.....

However I look through code base, and it's look like is not ready until you don't set manually open prop drop down list component. In getDerivedStateFromprops, next data list is read only if in next props is open set. to true

From DropDwonList

static getDerivedStateFromProps(nextProps, prevState) {
    let {
      open,
      value,
      data,
      messages,
      searchTerm,
      filter,
      minLength,
      caseSensitive,
    } = nextProps
    const { focusedItem } = prevState

    const accessors = getAccessors(nextProps)
    const valueChanged = value !== prevState.lastValue
    let initialIdx = valueChanged && accessors.indexOf(data, value)
//-->>  --- -- --- -- -- -- -- - - - - - - - - - --- - - --------
//-->>
    if (open)
      data = Filter.filter(data, {
        filter,
        searchTerm,
        minLength,
        caseSensitive,
        textField: accessors.text,
      })

    const list = reduceToListState(data, prevState.list, { nextProps })
    const selectedItem = data[initialIdx]
    const nextFocusedItem = ~data.indexOf(focusedItem) ? focusedItem : data[0]

    return {
      data,
      list,
      accessors,
      lastValue: value,
      messages: getMessages(messages),
      selectedItem: valueChanged
        ? list.nextEnabled(selectedItem)
        : prevState.selectedItem,
      focusedItem:
        (valueChanged || focusedItem === undefined)
          ? list.nextEnabled(selectedItem !== undefined ? selectedItem : nextFocusedItem)
          : nextFocusedItem,
    }
  }

I would try:

<DropDownList
 data={data}
 filter
 open
 valueField={id}
 textField={name}
 onSearch={onSearch}
 busy={searching} />
};

if it will be works, then you just have to manage your open state by yourself.

Robert
  • 2,538
  • 1
  • 9
  • 17
  • Not sure I follow. If I copy your DDL markup verbatim I still have the same issue. – user210757 Nov 17 '20 at 17:50
  • i think you have to pass filter function, when you tape in value in filter list then is created some fitler value, you have pass it to value of filter back. to check it try with some value of filter filter={"foo barr"} – Robert Nov 17 '20 at 18:09
  • yes that kind of worked i just passed (f) => f; but it still bombs when I use ListComponent prop when the dropdown is opened – user210757 Nov 17 '20 at 19:19
0

Got it! This issue is with the filter prop that you are passing to the component. The filter cannot take a true as value otherwise that would lead to abrupt behavior like the one you are experiencing.

enter image description here

This usage shall fix your problem:

      <DropdownList
        data={state.data}
        filter={() => true} // This was the miss/fix 
        valueField={"id"}
        textField={"name"}
        busy={state.searching}
        searchTerm={state.searchTerm}
        onSearch={(searchTerm) => setState({ searchTerm })}
        busySpinner={<span className="fas fa-sync fa-spin" />}
        delay={2000}
      />

Working demo

enter image description here

The entire code that I had tried at codesandbox:

Warning: You might have to handle the clearing of the values when the input is empty. I thought that the logic for this was irrelevant to the problem statement. If you want, I can update that as well.

Also, I added a fakeAPI when searchTerm changes that resolves a mocked data in 2 seconds(fake timeout to see loading state).

import * as React from "react";
import "./styles.css";
import { DropdownList } from "react-widgets";
import "react-widgets/dist/css/react-widgets.css";
// Coutesy: https://usehooks.com/useDebounce
import useDebounce from "./useDebounce";

interface IData {
  id: string;
  name: string;
}

const fakeAPI = () =>
  new Promise<IData[]>((resolve) => {
    window.setTimeout(() => {
      resolve([
        {
          name: "NA",
          id: "user210757"
        },
        {
          name: "Yash",
          id: "id-1"
        }
      ]);
    }, 2000);
  });

export default function App() {
  const [state, ss] = React.useState<{
    searching: boolean;
    data: IData[];
    searchTerm: string;
  }>({
    data: [],
    searching: false,
    searchTerm: ""
  });

  const debounceSearchTerm = useDebounce(state.searchTerm, 1200);

  const setState = (obj: Record<string, any>) =>
    ss((prevState) => ({ ...prevState, ...obj }));

  const getData = () => {
    console.log("getting data...");
    setState({ searching: true });
    fakeAPI().then((response) => {
      console.log("response: ", response);
      setState({ searching: false, data: response });
    });
  };

  React.useEffect(() => {
    if (debounceSearchTerm) {
      getData();
    }
  }, [debounceSearchTerm]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <DropdownList
        data={state.data}
        filter={() => true} // This was the miss/fix 
        valueField={"id"}
        textField={"name"}
        busy={state.searching}
        searchTerm={state.searchTerm}
        onSearch={(searchTerm) => setState({ searchTerm })}
        busySpinner={<span className="fas fa-sync fa-spin" />}
        delay={2000}
      />
    </div>
  );
}

Let me know if you have more queries on this

ypahalajani
  • 770
  • 1
  • 6
  • 18