LOTUSMS
LOTUSMS

Reputation: 10240

Material UI linear progress animation when using data

The docs at material ui for reactJS proposes this sample code for determinate progress bars.

export default class LinearProgressExampleDeterminate extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            completed: 0,
        };
    }

    componentDidMount() {
        this.timer = setTimeout(() => this.progress(5), 1000);
    }

    componentWillUnmount() {
        clearTimeout(this.timer);
    }

    progress(completed) {
        if (completed > 100) {
            this.setState({completed: 100});
        } else {
            this.setState({completed});
            const diff = Math.random() * 10;
            this.timer = setTimeout(() => this.progress(completed + diff), 1000);
        }
     }

     render() {
        return (
            <LinearProgress mode="determinate" value={this.state.completed} />
        );
     }
}

This creates a loading animation until bar is full. I am trying to modify it to use data from a json file so that it stops at the value that I specified for it in each json item. I am getting that part right. That was the easy part. But the animation fails because the animation is scripted using the value of completed in the constructor's state. And it is also located outside of my data.map so I can seem to find the way to make it read the value in the json file so it can se it for it's timeout function. :(

This is what I have (reduced)

JSON

exports.playerItems = [
    {
        id: 283,
        completed: "100",
    }
    {
        id: 284,
        completed: "70",
    }
    {
        id: 285,
        completed: "20",
    }

    ...

    {
        id: 295,
        completed: "50",
    }
]

Data injection

import PLAYERS from 'data/players.js';
const playersData = PLAYERS['playerItems'];

And my table is mapped

<Table>
   {playersData.map((row, index) => (
       <TableRow id={row.name} key={index}>
           <TableRowColumn>

                <LinearProgress
                    mode="determinate"
                    value={row.completed} />

            </TableRowColumn>
       </TableRow>
   ))}
</Table>

How can I modify the progress() function so that it animates the value given to the LinearProgress?

Thanks in advance

Upvotes: 2

Views: 12117

Answers (1)

Ken Gregory
Ken Gregory

Reputation: 7390

You can apply a state change to an array of player data and continually update the array in increments until all of the players have rendered.

First, start from zero:

  constructor(props) {
    super(props);
    this.state = {
      playersData: data.map(item => ({ ...item, completed: 0}))
    };
  };

Then initiate progress at mount:

  componentDidMount() {
    this.timer = setTimeout(() => this.progress(5), 100);
  }

Update until each player has reached 100%:

  progress(completion) {
    let done = 0;
    this.setState({
      playersData: data.map((item, i) => {
        const { completed: current } = this.state.playersData[i];
        const { completed: max } = item;
        if (current + completion >= max) {
          done += 1;
        }
        return {
          ...item,
          completed: Math.min(current + completion, max),
        };
      }),
    });
    if (done < data.length) {
      this.timer = setTimeout(() => this.progress(5), 100);
    }
  }

Adjust the delay and increment as you see fit. The limitation is that you need all of the player data that will be rendered and it needs to be in state as an array that is updated in a single setState

Here is a working example on codesandbox.

Upvotes: 6

Related Questions