Реагировать на ловушку useState с массивом, вызывая пропущенные вызовы - PullRequest
1 голос
/ 19 октября 2019

У меня есть следующий код:

function MyComponent( props ) {
    let arrSize = 5;
    const [arr, setArr] = useState( () => {
        let initial = new Array(arrSize);
        for(let i=0; i<arrSize; i++) initial.push({ foo: 'foo', bar: 'bar'});
        return initial;
    });

    mouseEventCallback = (e) => {
        // ...
        let tmp = arr;
        // ...
        setArr( tmp );
    }

    let cmp = [];
    for(let i=0; i<arrSize; i++) {  
       cmp.push( <span key={i} className={arr[i].foo}></span> );
    }

    return (
        <div>
           {cmp}
        </div>
    )
}

И когда несколько раз вызывается обратный вызов мыши, у меня возникает проблема: параметры состояния изменились нормально (проверка вкладки в веб-браузере Chrome с компонентами реагирования), но элементы DOMс этим состоянием не все отображаются повторно - некоторые из них имеют старое состояние.

Когда я добавляю фиктивное состояние - все работает идеально:

function MyComponent( props ) {
    let arrSize = 5;
    const [arr, setArr] = useState( () => {
        let initial = new Array(arrSize);
        for(let i=0; i<arrSize; i++) initial.push({ foo: 'foo', bar: 'bar'});
        return initial;
    });
    const [dummy, setDummy] = useState(0);

    mouseEventCallback = (e) => {
        // ...
        let tmp = arr;
        // ...
        setArr( tmp );
        setDummy( Math.rand() );
    }

    let cmp = [];
    for(let i=0; i<arrSize; i++) {  
       cmp.push( <span key={i} className={arr[i].foo}></span> );
    }

    return (
        <div dataTest={dummy}>
           {cmp}
        </div>
    )
}

Как я могу улучшить свой код и решить мою проблему с помощьюправильный путь?

1 Ответ

1 голос
/ 19 октября 2019

Ваш компонент не выполняет рендеринг после setArr (tmp), потому что переменная "tmp" ссылается на тот же массив, что и переменная "arr". Одна и та же ссылка означает, что они указывают на одну и ту же вещь в памяти, поэтому, когда реакция выполняет сравнение между старым состоянием и новым состоянием, они возвращают одно и то же, то есть tmp === arr возвращает true. Если старое состояние = новое состояние, компонент не перерисовывается. Итак, для того, чтобы перерисовать;сделать так, чтобы "tmp" указывал на новый массив с теми же данными, что и "arr". Вы можете использовать оператор распространения ES6.

Изменение: let tmp = arr;To: let tmp = [... arr];

let a = [1, 2, 3];
let b = a;
let c = [...a]; // ES6 Spread Operator

a === b; // returns true
a === c; // returns false

Правильный код:

function MyComponent( props ) {
    let arrSize = 5;
    const [arr, setArr] = useState( () => {
        let initial = new Array(arrSize);
        for(let i=0; i<arrSize; i++) initial.push({ foo: 'foo', bar: 'bar'});
        return initial;
    });

    mouseEventCallback = (e) => {
        // ...
        let tmp = [...arr];
        // ...
        setArr( tmp );
    }

    let cmp = [];
    for(let i=0; i<arrSize; i++) {  
        cmp.push( <span key={i} className={arr[i].foo}></span> );
    }

    return (
        <div>
            {cmp}
        </div>
    )
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...