Reputation: 10240
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
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