javascript - backgroundColor 不会重新渲染 - React

标签 javascript reactjs

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

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

enter image description here

但是当我使用 setState() 重置数组时,问题发生了,因为 react 重新渲染新数组非常好,但他从未将背景颜色渲染回白色。

这是我的组件:

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;
    }
}

最佳答案

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

因此,在您的情况下,您需要在状态中包含列值数组 (arrayToSort)、要交换的对数组 (sortedArrayAnim),以及一组先前更改的值 (prevChanged)。每当发生变化时, View 都会根据这些状态值进行更新。

演示(见代码注释):

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>

关于javascript - backgroundColor 不会重新渲染 - React,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58530676/

相关文章:

javascript - 例如,如何将字符串形式的已解析 promise 对象映射到按钮上

javascript - 将一个 JavaScript 对象分成两个单独的变量

javascript - 是否可以在 process.env 值中使用字符串文字?

javascript - 一次 POST Grails 中的所有传入参数

javascript - Node 表达 : not able to access data after query execution

reactjs - React Router V4 使用 Material UI 在 ListItem 内实现 NavLink

javascript - 悬停元素时过滤最后一个子元素

reactjs - 如何在 react-native 中呈现 Html 并更改数据?

Reactjs - 方法给出错误

reactjs - React - 如何将 `ref` 从子组件传递到父组件?