-
Notifications
You must be signed in to change notification settings - Fork 932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sometimes items is an empty array when clicking an item #186
Comments
Thanks for the issue @ferdinandsalis! Yeah, looking into this I've realized something kinda tricky about the way that downshift is implemented. We make the assumption that you'll render all items you need synchronously on every render. And you can make this work if the component you wrap is the whole downshift component rather than just the list (see this example and the Apollo example). If for some reason you'd rather connect your menu, then maybe there's something we can do... What if we exposed a In that example, I do some magic to simulate the built-in API, but it really would be as easy as calling If you like that, then I welcome you to make a PR to add it. Here's what you'd need to do:
Let me know what you think :) |
You could also add TypeScript types. |
Yeah, that'd be great! Should hopefully be pretty straightforward given the existing types and the simplicity of this API. It's just a function with no args and no return :) One other thing, you could replace this with a call to |
I am bit confused since I am doing the same thing as in your Apollo example, or am I missing something? Also could you elaborate more on the problem, I dont yet understand it and how |
The Apollo example wraps the component that renders Downshift keeps track of all the items on its own instance. It clears them every render and adds them all to the list as they're rendered. The problem is that you're rerendering the menu yourself (or rather Apollo is), so the items don't get cleared between renders, leading to unpredictable contents in the So if you want to be able to dynamically render items without rerendering all of downshift, you'll have to clear the items yourself. Of course, an alternative would be to wrap the whole component in Apollo (rather than just the menu) like the existing example... :) |
@kentcdodds thanks for explaining! I will take a look. |
Implemented in #187 |
That landed quickly. Thanks @kentcdodds. I guess I can close this. |
Thank you! 🎉 |
@kentcdodds sorry for being a pest but sadly this did not fix the issue. One more detail I noticed about the issue is that when using the cursor keys and enter to select an item it always works. I think it must have to do with the mouse over and the re-renders. Every little movement will lead to a re-render and clearing of items and adding of new items. Sometimes a click will then stumble upon an empty items array. I hope this is clear. Happy to chat about it. |
Yeah, that would be an issue... Do you not have any control over what props result in a new network request? You should only make a request when the input value prop changes... Does the higher order component have something for that? |
Yes that is what I am doing. Only if const withData = graphql(LIST_ITEM_SEARCH, {
skip: ({ inputValue }) => {
return inputValue == null || inputValue === '' ? true : false;
},
options: ({ inputValue, listId }) => ({
variables: { searchString: inputValue, listId },
fetchPolicy: 'cache-and-network'
})
}); |
That logic isn't preventing a rerender when the input doesn't change. It's preventing rerender when the input is falsy. |
Right, but why does this matter here. I have typed my search and received my results and now I want to select an item either by keyboard or mouse. Apollo wont do a network request at that point. I fear we are talking past each other. Sorry if my explanation is inadequate. Feel free to ignore this issue if you wish. |
Oh, I think I understand now! What if you only clear the items when it's loading? |
Or maybe only clear them after it's loaded? |
Would you be open to quickly discussing this via skype or similar? Anyway will try your suggestions and report back. |
No, that did not work either. I am lost |
Sorry, we just had our 4th baby, so I'm afraid I'm a bit limited with what I can do to help at the moment 😅 |
I'm using the new |
Hmmm.... I'm not certain that'd be best. I can't think of all use cases, but I think what would be better is for you to just call Otherwise we may make the wrong choice for people wanting to use the clearItems API in the future. |
Oh yeah, good call. Didn't even realize |
No worries and congratulations! I am still at number one 😊 |
Hi @kentcdodds, I looked at my problem again and I now understand that the complexity of the rendered children influenced wether I could select something with the mouse or not (apollo and the asynchronicity nature of the task was not an issue) . For example I am using styled-components right now and I have few of those styled components that make up an item (see below for code). Apparently this was enough that sometimes when clicking the item the internal item array was not yet populated. const Menu = ({
highlightedIndex,
selectedItem,
getItemProps,
data
}) => {
const { items, loading } = data;
if (loading || items.length === 0) {
return null;
}
return (
<List>
{items.map((item, index) => (
<Item
{...getItemProps({
item,
index,
key: item.id,
disabled: item.isIncluded,
active: R.equals(highlightedIndex, index),
selected: R.equals(selectedItem, item)
})}
>
<Work {...item.work} />
</Item>
))}
</List>
);
}; const Work = pure(({ cover, title, artist }) => (
<Flex>
<Cover width={48} height={48} image={cover} />
<Box ml={3}>
<Text mb={1} bold>
{title}
</Text>
<Text>{artist && artist.name}</Text>
</Box>
</Flex>
)); To mitigate this problem I have wrapped the Work component in pure hoc from recompose which implements Clearly this was not a problem of downshift. However I assume a few other people will encounter this problem. What do you think? What could downshift do? For example to reduce the probability of this issue couldn’t we debounce the mouseover event? Anyhow that all. I just wanted to give you a follow up. |
Thanks for following up. I'm a little confused by this:
What actually makes it so the list of items is not yet populated? |
Sorry my understanding of react internals and js are still limited. However my understanding is that on every render — which happens a lot when moving the mouse over the items — it also newly renders the children, thus there is quite a bit of work happening. So it should be possible that at the point of clicking an item |
I think I understand. So if items have a slow render method, then the user can click before things have finished rendering? Hmm... If you could dig a little deeper then that'd be great, but I think the best we can/should do is add documentation that says folks should implement Another thing you might consider is implementing windowing with react-virtualized. |
Yes will dig deeper. Will be a great way to learn more. Thanks for the feedback. |
@kentcdodds I'm dealing with the same issue. For me, from what I can tell, what happens is that when I click the first search result, it causes a new downshift render (which calls clearItems), and then subsequently rerenders my search results**. After that, the onClick handler executes and calls ** However, react-apollo has a shouldComponentUpdate, and it returns false for this rerender, so the items are cleared inside downshift, but there is no subsequent rerender so the items are left empty. I believe this theory to be the case because when I set my react-apollo component to have a prop such as |
Hi @dylanmoz, |
Huh, I think I ran into this same issue in a different context. In our case, we have a react Component inside our In our case, I guess the solution is to not use PureComponent, but this kind of makes me think that making |
Bingo, that's your solution. Please don't use PureComponent without an associated benchmark showing that it's necessary because otherwise it causes more problems than it's worth.
To be clear, it's not populating Seriously, check if you need the PureComponent implementation. You probably don't and it'll make your life easier to not use it. I'm really hesitant to add complexity to Downshift to support usage of PureComponent unnecessarily. |
That's a fair point, I do agree with that (wasn't my decision to use PureComponent there, I was just helping a coworker debug this issue). Premature optimization and all that ;) I do still feel like it can be confusing if you don't know how getItemProps works, because even if you're not using PureComponent any wrapper that implements My only suggestion then is that perhaps it's worth adding some kind of warning in the README about implementing Perhaps just adding something under the "What do you mean by impure function?" section like:
|
Yeah, I'd love an addition to the docs for that 👍 It could also say:
|
Just ran into a gnarly bug related to this. Posting this to hopefully help the next person. Honestly I don't think it has much to do with asynchronous filling of your items. No matter how the items are fetched, this will work fine ... IF, (and only if) the items re-render after the main Downshift component. Since these items are rendered in a child component of Downshift, you might be wondering how they could not. The answer is: if |
Just a heads-up that code like #186 (comment) is pretty fragile and can indeed break in the future. |
@kentcdodds - I think the solution here may be simple. Instead of a That would turn Let me know if you'd like a PR for that. |
The whole notion of calling methods during render sounds off to me. Why does the component need to store |
Think about things like highlighted index. As you hit the down arrow key, the next item in the list gets highlighted. But once you hit the end of the array, down key keeps the last item highlighted. But, thinking about it a bit more, surely items could be a regular prop, and then added to the render props, allowing devs to render whatever they want with them, right? |
We're going to look into fixing this. Tracking here: #599 |
Yeah I saw - thanks a ton for the amazing library. |
downshift
version: latestnode
version: 8.4npm
(oryarn
) version: 5.3.0What you did:
Select an item per left mouse click
What happened:
Most of the time it would select the item as expected. However sometimes it will not.
Reproduction repository:
https://codesandbox.io/s/l2yk1qqqrm
Problem description:*
This is due to the fact that at the point of time the item is clicked it has not yet been added to the items array.
Suggested solution:
No idea yet how to solve this.
The text was updated successfully, but these errors were encountered: