Skip to content
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

Getting Invalid warnings when creating own ref #324

Closed
zach446 opened this issue Sep 6, 2019 · 7 comments
Closed

Getting Invalid warnings when creating own ref #324

zach446 opened this issue Sep 6, 2019 · 7 comments
Labels
💬 question Further information is requested

Comments

@zach446
Copy link

zach446 commented Sep 6, 2019

What I Expect
When I am mid scroll, and the user changes the input, the scroll index should jump back to the top of the list once the new data loads.

What is happening
I was able to reset the scroll back to the top, but now I am getting warnings that the list ref and the onItemsRendered is invalid. Even though they are warnings they are hindering the list from populating. I have tested this by taking out the scrollToTop logic and replacing the List ref with the ref provided by InfiniteLoader.

Am I implementing the new ref incorrectly?

image
image

The Code

import React from 'react';
import PropTypes from 'prop-types';
import Downshift from 'downshift';
import styled from 'styled-components';
import { FixedSizeList as List } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
import MenuItem from '@material-ui/core/MenuItem';
import { withStyles } from '@material-ui/core/styles';

import BaseComponent from '../BaseComponent';

export class SearchFieldInfiniteScrollComponent extends BaseComponent {
  listRef = React.createRef();
  scrollToTop() { 
    this.listRef.current.scrollToItem({ align: 'start' });
  } 

  render() {
    const {
      dataArray,
      dropdownWidth,
      handleInput,
      handleSelection,
      isFirstListRender,
      isRowLoaded,
      itemContent,
      listHeight,
      loadedRowCount,
      loadingIndicator,
      loadMoreRows,
      placeholder,
      rowHeight,
      totalCount,
      threshold,
    } = this.props;

    return (
      <SearchContainer>
        <Downshift
          onChange={handleSelection}
          stateReducer={stateReducer}
          itemToString={item => (item ? item.value : '')}
        >
          {({ getInputProps, getMenuProps, isOpen, highlightedIndex, getItemProps }) => {
            return (
              <div>
                <Input
                  {...getInputProps({
                    onChange: e => {
                      if (!e.target.value.length) {
                        this.scrollToTop();
                      }

                      return handleInput(e);
                    },
                  })}
                  placeholder={placeholder}
                />
                <UnorderedList {...getMenuProps()}>
                  {isOpen && dataArray && (
                    <InfiniteLoader
                      isItemLoaded={isRowLoaded}
                      loadMoreItems={loadMoreRows}
                      itemCount={loadedRowCount}
                      threshold={threshold}
                    >
                      {({ onItemsRendered }) => (
                        <List
                          ref={this.listRef}
                          height={listHeight}
                          onItemsRendered={onItemsRendered}
                          itemCount={loadedRowCount}
                          itemSize={rowHeight}
                          width={dropdownWidth}
                        >
                          {({ index, style }) => {
                            let content = (
                              <Loading>
                                {(isFirstListRender || index < totalCount) && loadingIndicator}
                              </Loading>
                            );

                            if (dataArray[index]) {
                              content = (
                                <StyledMenuItem
                                  selected={highlightedIndex === index}
                                  component='div'
                                  {...getItemProps({
                                    key: itemContent,
                                    index,
                                    item: dataArray[index],
                                  })}
                                >
                                  {itemContent(index)}
                                </StyledMenuItem>
                              );
                            }

                            return (
                              <RowContainer key={`${index}-${Math.random()}`} style={style}>
                                {content}
                              </RowContainer>
                            );
                          }}
                        </List>
                      )}
                    </InfiniteLoader>
                  )}
                </UnorderedList>
              </div>
            );
          }}
        </Downshift>
      </SearchContainer>
    );
  }
}

export default withStyles(styles, { withTheme: true })(SearchFieldInfiniteScrollComponent);
@bvaughn
Copy link
Owner

bvaughn commented Sep 6, 2019

Please read the docs. The ref property needs to be attached to the inner list, as shown in the docs.
Screen Shot 2019-09-05 at 5 10 36 PM

This is what the warning message is talking about.

Fix this first, then if things are still broken- we can talk more.

@zach446
Copy link
Author

zach446 commented Sep 6, 2019

This is what I based the idea off of..
https://codesandbox.io/s/github/bvaughn/react-window/tree/master/website/sandboxes/scrolling-to-a-list-item

When I use the ref provided from the infiniteLoader I get the following error
image

For my component to work I need to call to the ref and reset the scroll when the input is emptied. By replacing listRef with the ref of the inner list I continuously receive that error.

Here is the code that I changed

...
export class SearchFieldInfiniteScrollComponent extends BaseComponent {
  listRef;
  scrollToTop() {
    this.listRef.current.scrollToItem({ align: 'start' });
  }

  render() { 
...

<InfiniteLoader
                      isItemLoaded={isRowLoaded}
                      loadMoreItems={loadMoreRows}
                      itemCount={loadedRowCount}
                      threshold={threshold}
                    >
                      {({ onItemsRendered, ref }) => {
                        this.listRef = ref;
                        return (
                          <List
                            ref={ref}
                            height={listHeight}
                            onItemsRendered={onItemsRendered}
                            itemCount={loadedRowCount}
                            itemSize={rowHeight}
                            width={dropdownWidth}
                          >

@zach446
Copy link
Author

zach446 commented Sep 6, 2019

I should mention that when I try to access the scrollToItem method with the inner ref within the List component I still get the above error

@nihgwu
Copy link
Contributor

nihgwu commented Sep 6, 2019

you should understand which ref is for which component, try <List ref={listRef => { this.listRef = listRef; ref(listRef); }} />

@bvaughn
Copy link
Owner

bvaughn commented Sep 6, 2019

ref is not what you think it is, as @nihgwu mentioned and as the docs I linked to above indicate :) It's a ref (function) you're meant to pass on and attach to the inner list so that infinite loader gets a ref to that list.

{({ onItemsRendered, ref }) => (
  <List
    ref={list => {
      ref(list); // Give InfiniteLoader a reference to the list
      this.listRef.current = ref; // Set your own ref to it as well.
    }}
  />
)}

I'm going to close this issue, since I'm pretty sure it's been been answered now. If you are still running into any additional problems, it would probably be best provide a complete runnable repro in Code Sandbox. :)

@taksenov
Copy link

taksenov commented Jul 27, 2020

ref={list => {
  ref(list); // Give InfiniteLoader a reference to the list
  this.listRef.current = ref; // Set your own ref to it as well.
}}

My case:
Instead of the this.listRef.current = ref; i'm wrote this.listRef.current = list;

InfntLdrHOC.tsx

import InfiniteLoader from 'react-window-infinite-loader';
...
<InfiniteLoader
  isItemLoaded={isItemLoaded}
  loadMoreItems={onScroll}
  itemCount={listCount}
>
  {({ onItemsRendered, ref }) => {
    return (
      <ListOfMessages
        config={{
          messages,
          height,
          width,
          ref,
          onItemsRendered,
          isFetchingNewScenario,
        }}
      />
    );
  }}
</InfiniteLoader>

ListOfMessages.tsx

import React from 'react';
import { VariableSizeList as List } from 'react-window';

import Message from '../Message';

class ListOfMessagesClass extends React.Component<any> {
  private listRef = React.createRef<any>();


  componentDidUpdate(prevProps: any) {
    const {
      isFetchingNewScenario: isFetchingNewScenarioPrev,
    } = prevProps.config;
    // eslint-disable-next-line react/destructuring-assignment
    const { isFetchingNewScenario } = this.props.config;

    if (isFetchingNewScenarioPrev && !isFetchingNewScenario) {
      this.scrollToNewMessage();
    }
  }


  scrollToNewMessage = () => {
    this.listRef.current!.scrollToItem(0);
  };

  render() {
    const { config } = this.props;
    const {
      messages = [],
      listCount,
      height,
      width,
      ref,
      onItemsRendered,
    } = config;

    const listLength = messages.length;

    return (
      <>
        <List
          height={height}
          itemCount={listLength}
          itemSize={(index: any) => messages[index].messageRenderHeight}
          width={width}
          ref={list => {
            ref(list); // Give InfiniteLoader a reference to the list
            // @ts-ignore
            this.listRef.current = list; // Set your own ref to it as well.
          }}
          onItemsRendered={onItemsRendered}
          itemData={messages}
          outerRef={this.outerRef}
        >
          {Message}
        </List>
      </>
    );
  }
}

export default ListOfMessages;

@sgup
Copy link

sgup commented Dec 15, 2021

Was also stuck on this, got auto-scroll to bottom like this:

const [listRef, setListRef] = useState(null)

// Scroll To Bottom
useEffect(() => {
  listRef?.scrollToItem?.(data.length - 1)
}, [listRef, data.length])

// ...

<>
  // ...
  {({ onItemsRendered, ref: setRef }) => {
    return (
      <FixedSizeList
        itemCount={data?.length}
        onItemsRendered={onItemsRendered}
        ref={(list) => {
          // @ts-ignore next-line
          setRef?.(list); // Gives the list ref to the parent InfiniteLoader
          setListRef(list);
        }}
        height={height}
        width={width}
        itemSize={50}
      >
        {RenderRow}
      </FixedSizeList>
    );
  }}
  // ...
</>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💬 question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants