Downshift: Changes to defaultSelectedItem prop does not impact re-rendered input

Created on 1 Mar 2018  路  5Comments  路  Source: downshift-js/downshift

Thanks for an awesome library! My issue may be related to incorrect usage of the downshift API, but after reviewing the docs, I'm still not sure of the proper way to handle this problem. In short, I'm finding changes in the defaultInput and defaultSelectedItem props in downshift do not result in changes in getInputProps and selectedItem in the arguments in the render prop.

Dependencies:

  • downshift version: 1.28.1
  • node version: 8.9.4
  • npm (or yarn) version: 1.5.1 (yarn)

What I did:

Please check out this forked example in codesandbox. Here's the relevant snippet:

<ExampleDownshift
  onStateChange={this.handleStateChange}
  // modifying to a controlled input (selectedItem) results in change
  defaultSelectedItem={{ name: this.state.defaultName }}
  onChange={this.handleChange}
  items={this.state.items}
  itemToString={this.itemToString}
/>

I'm then toggling a button that changes the state of the component that wraps ExampleDownshift.

What happened:

When ExampleDownshift re-renders and this.state.defaultName has a new value, the component rendered by Downshift's render prop does not render the new defaultSelectedItem. In the linked example, my expectation would be that Not Luke Skywalker becomes the new selectedItem instead of Luke Skywalker when pressing the Change default button.

Suggested solution:

I do not have a solution to offer, but my workaround right now is the following (sandbox example here):

const WrappedDownshift = () => (
  <ExampleDownshift
    onStateChange={this.handleStateChange}
    defaultSelectedItem={{ name: this.state.defaultName }}
    onChange={this.handleChange}
    items={this.state.items}
    itemToString={this.itemToString}
  />
);

When WrappedDownshift is rendered, this change now always results in a new Downshift being rendered. I think without this change, Downshift will just call componentWillReceiveProps on each render since it is already mounted, whereas this change will call the component constructor and reset this.state.selectedItem. I am not sure that changing state on componentWillReceiveProps is desirable behavior, but it would resolve this issue I think.

Thanks again for the awesome library; definitely want to be clear that this isn't necessarily a change request; it's asking how to use Downshift when you want to change the default selected input.

question

All 5 comments

Why not just change the selectedItem rather than changing the defaultSelectedItem? It is my understanding that the selectedItem is only set to the defaultSelectedItem value when Downshift is mounted in the constructor. If you want to change the selectedItem with an external button, then your best bet will be to make it a control prop.

What about something like this:

https://codesandbox.io/s/431w33k8ox

Note that the clearSelection button doesn't work in my example. The error isn't very helpful, so I'm not sure exactly what is breaking it, but I think it has something to do with selectedItem being set to null when it's shape should be {name: someValue}. Nonetheless, the codesandbox should be close to what you're looking for.

EDIT: I fixed the clearSelection issue :smile:

@austintackaberry selectedItem does resolve the issue, but using a control prop forces me to manage state; this is fine, but I'm wondering why it's not desirable functionality to make changes indefaultSelectedItem trigger changes in the displayed input. The button case is probably a bad example on my end here. What about a route change with react-router where Downshift's constructor is not invoked, but from the user perspective, it makes sense for some autocomplete to have a different value? For example, in a simple TODO app, say you have an autocomplete where you can search TODOs by name and URLs look like /todos/1. What if you want to pre-populate the current TODO in the autocomplete?

To be clear, I understand your point with control props, and if it doesn't make sense for Downshift to have the functionality I'm suggesting, I'll drop this issue 馃憤

@BenBrostoff This behaviour is in line with the behaviour of React's <input type="text" defaultValue={x} /> component as shown here https://codesandbox.io/s/ko1rm3wnk3.

As suggested above, you have the option of making it a controlled component. Adding code to detect changes in defaultSelectedItem would definitely improve the complexity of the component and would in fact overlap with selectedItem.

Thanks @donysukardi and @austintackaberry - these explanations make sense to me. I'll use a controlled component :+1:

Was this page helpful?
0 / 5 - 0 ratings