简体   繁体   中英

How to cycle className over children in React

I would like to use a setInterval to cycle which <li> object has a className of "showing", like this simple slideshow . I don't know how to do this in React-land.

function NumberList() {
  const numbers = [1, 2, 3, 4, 5];
  return (
    <ul>
      {
        numbers.map((number) =>
          <li key={number.toString()}>{number}</li>
        )
      }
    </ul>
  );
}


ReactDOM.render(
  <NumberList />,
  document.getElementById('root')
);

Anybody have any ideas?

Here's a working demo. Basically, you need to start a timer and keep the current showing list item in the state. The timer will increment the current number every 500ms and wrap around when it exceeds the number of items.

Some things to take note of:

  1. Use the alternative signature of this.setState because setState is not guaranteed to be synchronous and if you refer to this.state within setState it might be outdated.

  2. Remember to clear the timer upon unmount of the component.

 class App extends React.Component { constructor(props) { super(props); this.numbers = [1, 2, 3, 4, 5]; this.state = { current: 0, }; } componentDidMount() { this.timerId = setInterval(() => { this.setState(state => ({ ...state, current: (state.current + 1) % this.numbers.length, })); }, 500); } componentWillUnmount() { clearInterval(this.timerId); } render() { return ( <div className="App"> <ul> {this.numbers.map((number, index) => ( <li key={number} className={index === this.state.current ? 'slide showing' : 'slide'} > {number} </li> ))} </ul> </div> ); } } ReactDOM.render( <App />, document.getElementById('root') ); 
 .slide { font-size: 40px; padding: 40px; box-sizing: border-box; background: #333; color: #fff; display: none; width: 100%; height: 40px; } .slide.showing { display: inherit; } .slide:nth-of-type(1){ background: red; } .slide:nth-of-type(2){ background: orange; } .slide:nth-of-type(3){ background: green; } .slide:nth-of-type(4){ background: blue; } .slide:nth-of-type(5){ background: purple; } 
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"><div> 

You will probably need to use componentDidMount function to use the setInterval function so you have access to the classes. Also use ref to get the hold of the element itself.

https://reactjs.org/docs/refs-and-the-dom.html#when-to-use-refs

Within the componentDidMount get access to the element and then you should be able to any JS stuff.

demo: https://codesandbox.io/s/oopj96pv65

import React from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import "./style.css";

class App extends React.Component {
  componentDidMount() {
    var slides = this.elem.children;
    var currentSlide = 0;

    setInterval(() => {
      slides[currentSlide].classList.remove("showing");
      currentSlide = (currentSlide + 1) % slides.length;
      slides[currentSlide].classList.add("showing");
    }, 2000);
  }

  render() {
    return (
      <ul
        id="slides"
        ref={elem => {
          this.elem = elem;
        }}
      >
        <li className="slide showing">Slide 1</li>
        <li className="slide">Slide 2</li>
        <li className="slide">Slide 3</li>
        <li className="slide">Slide 4</li>
        <li className="slide">Slide 5</li>
      </ul>
    );
  }
}

render(<App />, document.getElementById("root"));

Building on your simple example:

// Store the state outside of the components
const numbers = [1, 2, 3, 4, 5];
let activeNumber = 0;

// A stateless NumberList component takes the numbers and activeNumber props
function NumberList(props) {
    // The `active` class is conditionally added to the appropriate `li`
    return (
        <ul>
            {
                props.numbers.map((number) =>
                    <li key={number} className={props.activeNumber === number ? 'active' : ''}>
                        {number}
                    </li>
                )
            }
        </ul>
    );
}

// Render method takes in the active number
function render(activeNumber) {
    ReactDOM.render(
        <NumberList numbers={numbers} activeNumber={activeNumber} />,
        document.getElementById('root')
    );
}

// We set an interval timer to update activeNumber and re-render
setInterval(function() {
    if (activeNumber < numbers.length) {
        activeNumber++
    } else {
        activeNumber = 1;
    }
    render(activeNumber);
}, 1000);

render(); // Initial render

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM