我正在构建一个排序算法可视化程序。这些项目中的每一项都是div
,其中backgroundColor
设置为白色。
算法运行时,他将backgroundColor
设置为橙色以显示哪些项目已更改。
但是当我使用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;
}
}
答案 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>