react-scrollspy icon indicating copy to clipboard operation
react-scrollspy copied to clipboard

List Items generated programmatically

Open lanceschi opened this issue 7 years ago • 10 comments

Hi @makotot ,

first of all thanks for the beautiful component! I took a look at the issues unsuccessfully. I'd like to make react-scrollspy work together with programmatically generated list items. Could you plese provide me some info about the feasability. Thanks in advance. Here you'll find a reference code sample to give you a rough idea of what I'm talking:

const Parent = ({options}) => (
  <Scrollspy
    items={ ['news', 'mission', 'whatwedo', 'competences'] }
    currentClassName="active">
      {options.map((x, idx) => <Child item={x} key={idx}/>)}
  </Scrollspy>
);

const Child = ({item}) => (
  <li>
    <a href={item.anchor}>{item.label}</a>
  </li>
);

lanceschi avatar Dec 07 '17 09:12 lanceschi

What I came up with as a work-around is to intercept Scrollspy onUpdate method. I hook into the onUpdate method in order to update a newly created active attribute in the hash array of Scrollspy <ul> option list items.

I'll welcome a better approach.

Best, Luca

lanceschi avatar Dec 07 '17 14:12 lanceschi

@lanceschi Sorry, I have no time at this weekday, so I'll check the issue at this weekend.

makotot avatar Dec 07 '17 16:12 makotot

I think using onUpdate is only way to accomplish your needs. Unfortunately, this lib is not thought about generated list items at least now.

  • With generated list items, currentClassName does not pass to target list element. 2017-12-10 21 58 47

  • With static list items, currentClassName definitely passes to target. 2017-12-10 21 59 36

makotot avatar Dec 10 '17 13:12 makotot

@makotot thanks! Got it. I guess you can close this issue now.

If you're planning on extending this lib in order to support programmatically generated option items, then let me know. I'd like to help and contribute.

Kind regards, Luca

lanceschi avatar Dec 10 '17 16:12 lanceschi

@lanceschi I am also trying to use this library with generated list items. Would you mind sharing how you implemented the work-around you mentioned above? I am trying to console.log the output of any variables in the onUpdate event, but I don't see anything...

gtwilliams03 avatar Feb 03 '18 03:02 gtwilliams03

Hi @gtwilliams03 onUpdate is to deliver a parameter (object). Here you'll find some code in order to give you an idea of how I implemented the workaround:

Partial JSX markup within the component render function

<Scrollspy
  items={this.optionIdList}
  offset={-160}
  onUpdate={this.setActiveOption}
  currentClassName="active">
    {this
      .state
      .options
      .map((x, idx) => <MenuOption item={x} key={idx}/>)}  
</Scrollspy>

OnUpdate event handler function

I read and map the list of the menu options (array of objects) from this.props.options and eventually set the active flag and set as a state of the component. this.state.options is used to programmatically render the menu options as you can see in the above jsx snippet.

setActiveOption(e) {
  if (!e) return;
  const optHash = this
    .props
    .options
    .map(x => (x.anchor === `#${e.id}`)
      ? {
        ...x,
        active: true
      }
      : x);
  this.setState({opts: optHash});
}

I hope it'll help you!

Best, Luca

lanceschi avatar Feb 03 '18 20:02 lanceschi

@lanceschi Thank you for the note! I get undefined in the onUpdate handler when I write out e to the console (only when the component mounts, and handleScrollSpyChange is not called if the page is scrolled (I am sure I am making a basic mistake...):

    handleScrollSpyChange(e) {
        console.log(e)
    }

    render() {
        const { items } = this.props
        return (
            <div>                        
                <Scrollspy items={items.map(i => i.abbreviation)} offset={-100}
                    currentClassName='active' onUpdate={this.handleScrollSpyChange}>
                    {items.map(i => <li key={`${i.abbreviation}`}><a href={`#section-${i.abbreviation}`}>{i.abbreviation}</a></li>)}
                </Scrollspy>
            </div>
        )
    }

gtwilliams03 avatar Feb 05 '18 22:02 gtwilliams03

@makotot can you:

  • further explicit what's inside items attribute of Scrollspy;
  • define your component constructor function? Do you bind that handleScrollSpyChange function?

Best, L

lanceschi avatar Feb 06 '18 22:02 lanceschi

Was so stumped that my generated markup wasn't working. This would be good to put in the docs on how to use with programmatically created list objects.

I realised that it if you map through an array and return the JSX - not using a functional Component it works. I'm intrigued as to why this happens? 🤷‍♂️🤓

const items = ['section-1', 'section-2', 'section-3']

const links = items.map(link => {
  return <li><a href={link}>{link}</a></li>
})

...

<Scrollspy
  items={ ['section-1', 'section-2', 'section-3'] }
  currentClassName="is-current"
  className="c-side-nav__list nav-list"
  style={ {fontWeight: 300} }
  offset={ -20 }
  onUpdate={
    (el) => {
      console.log(el)
    }
  }
>
  {links}
</Scrollspy>

karltaylor avatar Feb 26 '18 11:02 karltaylor

Related to https://github.com/makotot/react-scrollspy/issues/96

thebarty avatar Mar 07 '19 08:03 thebarty