0

I have a div, with player score, deaths and assists:

    <div className="liveMatchPlayerScore">
      {data.kill_count}/{data.death_count}/{data.assists_count}
    </div>

Every time the kill or death count changes, I'd like the text to turn a bold white for 3 seconds.

I was thinking of using react-spring for this, specifically useTransitions, but the documentation shows examples using an array of items. I going to put each of the scores in an array, but it seems counterproductive.

Previously i tried wrapping the scores in an "Spring" component from react-spring but that only animated the scores on their initial render - not when they update.

How can I make the kill_count and death_count become white for 3 seconds upon changing value?

Thank you

I used @PeterAmbruzs solution, but i seem to be getting strange numbers. For example in the images below, first the score was 0/0/0 and the first number increased by 1. Instead of becoming 1/0/0, it became 01/0/0. I'm also getting absurdly high numbers for some reason. Does anyone know why this might be happening?

Before What it should be

Noob
  • 754
  • 3
  • 10
  • 27
  • How about using react-transition-group? (https://github.com/reactjs/react-transition-group/tree/v1-stable) – Álvaro Tihanyi Jan 23 '20 at 16:06
  • So essentially with react-transition-group I'd just wrap the kill and death count in a `` and specify the changes i'd like? I can't seem to find a value for color change. – Noob Jan 23 '20 at 16:08
  • you can do it basic using css, just change the classname when you receive props – mba3gar Jan 23 '20 at 16:14
  • @mba3gar could you explain this a little bit more in depth? – Noob Jan 23 '20 at 16:16
  • let me show you an example first make sure that your data are already registered in the state, then wrap each element in a p or a span based on your context or view and give it a class name based on it's current state for example `

    1} ? `blackColor` : `whiteColor` `}>

    `
    – mba3gar Jan 23 '20 at 16:23
  • @mba3gar my data isnt registered in the state. The data is passed into my component through props. Also, how would i make the text go back to black after 3 seconds? – Noob Jan 23 '20 at 16:54

2 Answers2

2

I have also a solution. I think it is quite simple. First you create a component for the animated numbers. I wrapped it in react.memo to update it only when its property change. You can see it is bold, and red at start, but after 3sec it became normal and black. But you can change the style whatever you want. I added skip property to prevent animation for example for the first render.

const FadeNumber = React.memo(({ value, skip }) => {
  const style = useSpring({
    from: { color: "red", fontWeight: "bold" },
    to: { color: "black", fontWeight: "normal" },
    delay: 3000
  });

  return (
    <animated.span style={{ margin: "10px", ...(skip ? {} : style) }}>
      {value}
    </animated.span>
  );
});

Now there is a trick to reRender the animation at property change. Simly put the value to the key. When the key changes a new component will be created, so the animation will be played again. I added a unique prefix to prevent side effects.

<FadeNumber skip={kill === 0} key={"a" + kill} value={kill} />

And the whole example:

https://codesandbox.io/s/react-spring-change-fade-out-j8ebk

Peter Ambruzs
  • 7,763
  • 3
  • 30
  • 36
  • Awesome, is there somewhere we can chat so I can show you a bit more of my code? The values are coming from props. I am mapping props and pulling props.data.kill or props.data.death from it. I can store the values in a variable, but i wouldnt be able to store it within state since its within a map right? – Noob Jan 23 '20 at 23:25
  • Sorry not today. It is late night here. But you can use properties as values, no need to create state from them. – Peter Ambruzs Jan 23 '20 at 23:27
  • No worries, i managed to get it working just fine. That was very easy, thank you so much for the elegant solution! I have one issue: When the page initially loads, all the text is red. Is there a way to stop the text from being a different color on initial load? Thanks! – Noob Jan 24 '20 at 00:01
  • Awesome! There’s also another side effect. There are multiple GameList components on the page. Each one has a FadeNumber component. My webpage automatically renders new GameList components when a new game is found without having to refresh the page. When a new GameList component is rendered it causes my other GameList components to move and subsequently they change color. Do you know how to fix this? Thanks! PS - feel free to check out the website. www.dotatracker.com so you can see for yourself! – Noob Jan 24 '20 at 14:26
  • perhaps there’s a `skip = {first}` type of command to skip the first prop change. – Noob Jan 24 '20 at 15:32
  • Please let me know if you manage to check my 2 previous comments. Thank you! – Noob Jan 24 '20 at 21:01
0

https://stackblitz.com/edit/react-5sztov?file=index.js

setTimeout() is still a viable construct - even when working in React.

import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';

class App extends Component {
  constructor() {
    super();
    this.state = {
      assistsCount: 0,
      color: 'black',
      deathCount: 0,
      fontWeight: 'normal',
      killCount: 0,
      setStyleToReset: false,
    };
  }

  increaseKillCount () {
    this.setState((prevState, props) => {
      return {
        killCount: prevState.killCount + 1,
        color: 'white',
        fontWeight: 'bold',
        setStyleToReset: true,
      };
    });
  }

  render() {
    if (this.state.setStyleToReset) {
      this.resetStyle();
    }
    return (
      <div>
        <div style={{
          backgroundColor:'green',
        }}>
          <span style={{color:this.state.color, fontWeight:this.state.fontWeight}}>{this.state.killCount}</span>/{this.state.deathCount}/{this.state.assistsCount}
        </div>
        <button onClick={() => this.increaseKillCount()}>Increase Kill Count</button>
      </div>
    );
  }

  resetStyle() {
    setTimeout(() => {
      this.setState({
        color: 'black',
        fontWeight: 'normal',
      });
    }, 3000);
  }
}

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