tbranyen / hyperlist

A performant virtual scrolling list utility capable of rendering millions of rows
Other
448 stars 45 forks source link

Remove ReactFauxDOM dependency for React usage and create npm module for React helper component #38

Open thcolin opened 7 years ago

thcolin commented 7 years ago

Hi !

My friend @soyuka told me about your library for my current React project, and he show me your ongoing pull request to improve React support without interfering too much with current implementation (Some initial work on React support #8).

So I work a bit on it and here's my suggestion :

This solution should allow you to support various not DOM-friendly frameworks in the long term.

If you're okay with my changes, I'll make another commit to remove react-example.html and react specific dev-dependecies and then create a hyperlist-react npm module to bundle HyperListReact React component and usage documentation. It would be helpful to have a link to futur hyperlist-react repo on README project too.

HyperListReact component would look like this :

class HyperListReact extends React.Component {
  render() {
    return React.createElement('div', this.state.element,
      this.state.fragment
    );
  }

  constructor(props) {
    super(props);

    this.handleRef = this.handleRef.bind(this);

    this.state = {
      element: {
        ref: this.handleRef
      },
      scroller: {
        key: 'scroller',
        style: {
          position: 'relative'
        }
      },
      fragment: [],
      node: {}
    };
  }

  handleRef(node) {
    this.setState({ node })
  }

  componentDidMount() {
    const {
      height = window.innerHeight,
      itemHeight = 50,
      total = 0,
      reverse = false,
      generate = () => {}
    } = this.props;

    const config = {
      height,
      itemHeight,
      total,
      generate,
      scroller: React.createElement('div', this.state.scroller),
      useFragment: false,
      overrideScrollPosition: () => this.state.node.scrollTop || 0,
      inspectElement: (element, key) => element.props[key === 'class' ? 'className' : key] || null,
      transformElement: (element, values) => {
        if (values.class) {
          values.className = values.class
          delete values.class
        }

        return React.cloneElement(element, values)
      },
      mergeStyle: (element, style, forceClone) => {
        if (forceClone) {
          return React.cloneElement(element, { style })
        }

        for (let i in style) {
          if (element.props.style[i] !== style[i]) {
            element.props.style[i] = style[i]
          }
        }

        return element
      },
      applyPatch: (element, fragment) => {
        this.setState({
          element: Object.assign({}, this.state.element, {
            style: Object.assign({}, this.state.element.style, element.props.style)
          }),
          fragment
        });
      },
    };

    this.list = HyperList.create(this, config);

    // Bind to the resize event, and since you should only ever have one
    // handler bound to this, we pave over whatever you had set before.
    window.onresize = e => {
      config.height = window.innerHeight;
      this.list.refresh(this, config);
    };
  }

  componentWillUnmount() {
    window.onresize = null;
    this.list.destroy();
  }
}

HyperListReact.defaultProps = {
  style: {}
}

With same usage as #8 (see examples/react-example.html for full example) :

class MyComponent extends React.Component {
  render() {
    return React.createElement(HyperListReact, {
      itemHeight: 30,
      total: 100000,
      generate: this.generate
    })
  }
  generate(row) {
    return React.createElement('div', {}, 'Item ' + row);
  }
}
soyuka commented 7 years ago

wdyt @tbranyen ?

tbranyen commented 7 years ago

Looks good to me. I think I may have just caused a conflict by merging. Can we get this rebased to master and I'll give it a whirl in my app!

soyuka commented 7 years ago

@tbranyen I've worked a bit more on this and rebase will come in a couple of days ;)

thcolin commented 7 years ago

Sorry, I'll won't be able to complete this PR, I tried last week without any luck, so I made my own in React way, sorry 😔 I deprecated my repo thcolin/hyperlist-react, but anyone who want to start from where I was can fork it of course ! I tried tounpublish the library on npm but they changed their policy so I could only deprecate it but see :

If another member of the community wishes to publish a package with the same name as a security placeholder, they’ll need to contact support@npmjs.com. npm will determine whether to grant this request. (Generally, we will.)

Sorry for any false hope 😔

tbranyen commented 7 years ago

@thcolin I'm going to reopen this since it's interesting work and maybe someone else can pick it up. It's open source after-all.