backgroundColor不会重新渲染-React

时间:2019-10-23 20:37:52

标签: javascript reactjs

我正在构建一个排序算法可视化程序。这些项目中的每一项都是div,其中backgroundColor设置为白色。 enter image description here

算法运行时,他将backgroundColor设置为橙色以显示哪些项目已更改。

enter image description here

但是当我使用setState()重置数组时会发生问题,因为react可以很好地重新渲染新数组,但是他从未将backgroundColor渲染回白色。

这是我的组件:

import React from 'react'
import './SortingVisualizer.css'
import InsertionSort from '../SortingAlgorithms/InsertionSort'

const NORMAL_COLOR = 'white';
const CHANGED_COLOR = 'red';
const AFTER_CHANGE_COLOR = 'orange';

export default class SortingVisualizer extends React.Component{

    constructor(props){
        super(props);

        this.state = {
            arrayToSort: []
        };
    }

    componentDidMount(){
        this.resetArray();
    }

    resetArray(){
        const arrayToSort = [];
        for (let i = 0; i < 200; i++) {
            arrayToSort.push(this.RandomIntBetweenRange(5, 1000));
        }
        this.setState({ arrayToSort });
    }

    insertionSort(){
        let sortedArrayAnim = InsertionSort(this.state.arrayToSort);
        let arrayToSort = this.state.arrayToSort;
        let arrayBars = document.getElementsByClassName('array-item');
        let arrayBarsWithColorChanged = [];

        //loop through all the animations
        for (let index = 0; index < sortedArrayAnim.length; index++) {
            const [i,j] = sortedArrayAnim[index];

            //setTimeout(() => {
                //set changed colors back to normal
                if(index !== 0){
                    arrayBarsWithColorChanged.forEach((element, index) => {
                        arrayBars[element].style.backgroundColor = AFTER_CHANGE_COLOR;
                    });
                    arrayBarsWithColorChanged = [];
                }

                let temp = arrayToSort[i];
                //change array
                arrayToSort[i] = arrayToSort[j];
                arrayToSort[j] = temp;

                //change div bar colors, unl
                if(index != sortedArrayAnim.length - 1){
                    arrayBars[i].style.backgroundColor = CHANGED_COLOR;
                    arrayBars[j].style.backgroundColor = CHANGED_COLOR;
                    arrayBarsWithColorChanged.push(i);
                    arrayBarsWithColorChanged.push(j);
                }
                this.setState({ arrayToSort })
            //}, 10);
        }
    }

    render() {
        const {arrayToSort} = this.state;

        return (
            <div className="main-div">
                {arrayToSort.map((value, idx) => (
                    <div className="array-item" key={idx} style={{height: value, backgroundColor: 'white'}}>

                    </div>
                ))}

                <button onClick={() => this.resetArray()}>Generate new array</button>
                <button onClick={() => this.insertionSort()}>Insertion Sort</button>
            </ div>
        );
    }

    RandomIntBetweenRange(min, max){
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
}

2 个答案:

答案 0 :(得分:1)

您将希望将所有内容保留在state中,以便根据对state所做的任何更改来重新渲染。

state = {
  backgroundColor: 'white'
}

...

// element
<div className="array-item" key={idx} style={{height: value, backgroundColor: this.state.backgroundColor}}>

arrayBars[i].style.backgroundColor = 'orange';替换为this.setState({ backgroundColor: 'orange' });

并更新:

resetArray(){
    const arrayToSort = [];
    for (let i = 0; i < 200; i++) {
        arrayToSort.push(this.RandomIntBetweenRange(5, 1000));
    }
    this.setState({ arrayToSort, backgroundColor: 'white' }); // reset back to white
}

答案 1 :(得分:1)

在React中,您不会直接操作DOM,也不会更改状态(例如,arrayToSort[i] = arrayToSort[j])。您更改模型(状态,道具),视图也会相应更改。

因此,在您的情况下,您需要在状态中包括列值数组(arrayToSort),要交换的对数组(sortedArrayAnim)和一组先前更改的值(prevChanged)。每当发生更改时,视图将根据这些状态值进行更新。

演示(请参阅代码中的注释):

const NORMAL_COLOR = 'white';
const CHANGED_COLOR = 'red';
const AFTER_CHANGE_COLOR = 'orange';

/** this function return the color **/
const getColor = (idx, prevChanged, [current]) => {
  if(current && current.includes(idx)) return CHANGED_COLOR; // if it's in current changed pair [i, j]

  if(prevChanged.has(idx)) return AFTER_CHANGE_COLOR; // if it was changed before
  
  return NORMAL_COLOR;
}

class SortingVisualizer extends React.Component {
  state = {
    arrayToSort: [],
    sortedArrayAnim: [],
    prevChanged: new Set()
  };
  
  timeout = null;

  componentDidMount() {
    this.resetArray();
  }

  resetArray = () => {
    clearTimeout(this.timeout);
  
    const arrayToSort = [];
    for (let i = 0; i < 50; i++) {
      arrayToSort.push(this.RandomIntBetweenRange(1, 100));
    }
    this.setState({
      arrayToSort,
      sortedArrayAnim: [],
      prevChanged: new Set()
    });
  }
  
  animate = (sortedArrayAnim) => {
    this.setState(
      ({
        prevChanged,
        arrayToSort 
      }) => {
        if(!sortedArrayAnim.length) return { sortedArrayAnim };
      
        const [current] = sortedArrayAnim;
        const newArrayToSort = [...arrayToSort]; // clone newArrayToSort
        
        /** flip the values according to current change [i, j] **/
        newArrayToSort[current[0]] = arrayToSort[current[1]];
        newArrayToSort[current[1]] = arrayToSort[current[0]];
      
        return ({
          arrayToSort: newArrayToSort,
          sortedArrayAnim,
          prevChanged: new Set([...prevChanged, ...current]) // add changed items to the Set
        });
      },
      () => { // when state change is done
        const { sortedArrayAnim } = this.state;

        // if there are more items to change wait and call animate again
        if(sortedArrayAnim.length) {
          this.timeout = setTimeout(() => this.animate(sortedArrayAnim.slice(1)), 1000); 
        }
      }
    );
  }

  insertionSort = () => {
    const sortedArrayAnim = [[1, 5], [10, 15], [20, 13], [17, 48], [20, 13], [45, 17]]; // InsertionSort(this.state.arrayToSort); // I've used a dummy array
  
    this.animate(sortedArrayAnim);
  }

  render() {
    const { arrayToSort, sortedArrayAnim, prevChanged } = this.state;

    return (
      <div className="main-div">
      {arrayToSort.map((value, idx) => (
        <div className="array-item" key={idx} style={{
          height: `${value}vh`,
          backgroundColor: getColor(idx, prevChanged, sortedArrayAnim)
        }}>

        </div>
      ))}

        <button onClick={this.resetArray}>Generate new array</button>
        <button onClick={this.insertionSort}>Insertion Sort</button>
      </ div>
    );
  }

  RandomIntBetweenRange(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

ReactDOM.render(
  <SortingVisualizer />,
  root
);
.main-div {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  background: black;
  height: 100vh;
  padding: 1vmax 1vmax 0 ;
  box-sizing: border-box;
}

.array-item {
  width: 1vw;
  transition: height 0.3s;
}

button {
  align-self: flex-start;
}

body {
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>