1

Тема: React: не працює state setter

Як таке може бути взагалі?
Коду багато, не можу його весь сюди привести (якщо щось непомінтно впливає на ситуацію, то я не знаю). Версія React 16.12.0.

Ось такий функціональний компонент з хуком useState:

import React, { useState, createRef } from 'react';
import { Table, Input, Button, Radio, InputNumber, DatePicker, Spin, Icon, Modal } from 'antd';
...
export default function DirectoryTable({ path, refresh, onClick, onFooterMenu }) {
...
    const [ destination, setDestination ] = useState('');
...
    const onDestinationSelect = data => {
        console.log(data);
        console.log(typeof data);
        setDestination(data);
        console.log(destination);
    }

    const doCopy = () => {
        console.log(destination);
        //setDestination('');
    }

    const copyClick = event => {
        confirm({
            title: i18next.t('copy_destination'),
            content: <DestinationModal onSelect={ onDestinationSelect } refresh={ refresh } />,
            onOk() {
                doCopy();
            },
            onCancel() {
                //setDestination('');
            }
        });
    }
....
    const rowSelection = {
        selectedRowKeys,
        onChange: keys => {
            setSelectedRowKeys(keys);
            onFooterMenu([<FooterMenu key="footer" visible={keys.length > 0} onClickCopy={ copyClick } />]);
        },
...

У компоненті FooterMenu на клік по відповідному елементу спрацьовує onClickCopy={ copyClick }, там викликається модальне вікно для вибору шляху призначення, в якому є and.design'овський компонент Select з обробником onSelect={ onDestinationSelect } який в параметрі отримує рядок зі значенням шляху призначення, я там навтикав console.log'ів, щоб бачити значення стану destination до та після виклику сеттера. Також після натискання кнопки Ok спрацьовує обробник doCopy, де я втикнув ще один console.log - раптом якась затримка в часі виникає. З цією ж метою, я роблю декілька циклів: клік-вибір-ок і в коснолі я бачу, що весь час в стані destination зберігається початкове значення <empty string>:

/test/ directory-table.jsx:52
string directory-table.jsx:53
<empty string> directory-table.jsx:55
<empty string> directory-table.jsx:59
XHRGEThttp://filemanager.com/api/tree
[HTTP/1.1 200 OK 1188ms]

/test/test01/ directory-table.jsx:52
string directory-table.jsx:53
<empty string> directory-table.jsx:55
<empty string>

WTF?

2 Востаннє редагувалося Yola (10.10.2020 19:41:07)

Re: React: не працює state setter

А ви пробували робити обробники не лямбдами.

Також, я підозрюю, що setDestination працює асинхронно для того, щоб встановлювати стан лише один раз навіть, якщо ви викликали set... багато разів. Через це ви не маєте читати зі стану. Тобто, після встановлення ви можете читати стан лише в наступній відмальовці компонента.

3

Re: React: не працює state setter

Yola написав:

А ви пробували робити обробники не лямбдами.

А з чого це вони лямбди? Може я не зовсім розумію що таке лямди, в моєму розумінні стрілочний синтаксис не робить функції лямбдами, тобто анонімними функціями. Якби я ліпив їх безпосередньо в компонентах, тоді б вони були лямбдами. Але, все одно дякую, я спробую замінити на старий синтаксис, можливо бабєль щось не те мутить з цими стрілками.

Також, я підозрюю, що setDestination працює асинхронно для того, щоб встановлювати стан лише один раз навіть, якщо ви викликали set... багато разів. Через це ви не маєте читати зі стану. Тобто, після встановлення ви можете читати стан лише в наступній відмальовці компонента.

Ось це для мене взагалі дивно. Сама зміна стану має викликати наступну відмальовку, чи не так? Тож, при повторному виклику стан мав би бути вже змінений. Я ж в лозі привів два цикли клік-селект-ок, виконані підряд і в обох циклах значення стану залишилось незмінним.

Я нічого не можу зрозуміти, чому це відбувається... з моєї точки зору, такого не мало би бути. Мабуть, я поганий програміст, бо в мене такі ситуації не викликають азарту (щоб доковирятися до суті), а навпаки, виникає бажання все закинути до біса... Все має працювати так, як задокументовано, а якщо щось отак стає впоперек, то опускаються руки... (

4

Re: React: не працює state setter

PS: А... здається я зрозумів, що ви мали на увазі... Що це саме змінні-функції... так, мабуть, то і є лямбди. Останнім часом постійно їх всюди використовую (бо використовують мої колеги), наче ще не було ні разу таких проблем. Але я дійсно спробую замінити на функцію. Цікаво чи допоможе.

5

Re: React: не працює state setter

Переписування отих лямбд на звичайні функції не допомогло :(

6 Востаннє редагувалося Yola (12.10.2020 15:49:41)

Re: React: не працює state setter

bvn написав:

Все має працювати так, як задокументовано, а якщо щось отак стає впоперек, то опускаються руки... (

Ну, там так і написано

Документація написав:

React може групувати кілька викликів setState() в одне оновлення для продуктивності.

Оскільки this.props і this.state можуть бути оновлені асинхронно, не варто покладатися на їх значення для обчислення наступного стану.

Гадаю, що у функціях так само.

Тобто, невідомо, що виведе оцей шмат коду:

setDestination(data);
console.log(destination);

Можу погодитись, що документація по реакту далека від ідеалу, і він продовжує змінюватись, а потім ще треба буде підключати бібліотеки у яких теж не найкраща документація. Але з часом воно піде легше, значно легше.

Подякували: flatliner1

7 Востаннє редагувалося flatliner (12.10.2020 21:36:39)

Re: React: не працює state setter

Ні, про оцю асинхронність я знаю... це якщо стан змінюється дуже часто - я навіть з цим зтикався, коли замість декількох тисяч змін станів, я отримував одну в кінці процесу, доводилось поза реактом в DOM влазити, щоб відобразити потік даних у вікні-журналі. В кінці-кінців, моє втручання перезатиралось кінцевим станом і все було ок.

Але це не той випадок. Тут, між вибором destination і натисканням кнопки Ok може пройти скільки завгодно довгий час, можна виконать ції дії кілька разів по черзі, стан не змінюється... хоча на справді змінюється.
Сьогоднішні мої експерименти показали, дещо дивний результат, я поки що не зрозумів до кінця що не так і як це виправити, але таке враження, що все залежить від певного контексту... щось на кшталт не співпадіння області видимості стану... звучить, як марення.

Коротше, я додав до компоненту DirectoryTable вивід стану destination у футері таблиці. Так от він змінюється моментально, тільки я обираю шлях у модальному вікні. Але виходить, що в самому обробнику кліка на кнопці, обробнику вибору шляху та обробнику натискання на кнопку Ok я прочитати цей стан не можу... Щось я там перемудрив, але не можу до кінця зрозуміти що. Можливо, треба поміняти форму модальних вікон з функціональної confirm(...) на компонентну <Modal visible={...} ... >.

8 Востаннє редагувалося Yola (12.10.2020 21:51:42)

Re: React: не працює state setter

А якщо onCopy замінити не щось таке

    onOk() {
        function Call(callee) { callee(); }
        Call(doCopy);
    }

Я не дуже розбираюсь в лямбдах і не знаю як влаштований confirm, але можна спробувати.

Подякували: flatliner1

9

Re: React: не працює state setter

А ще стан таки змінюється, коли відмічаєш новий рядок у таблиці, або знімаєш відмітку з рядка... Тобто виходить така штук, що оновлений стан "не доходить" до тієї частини дерева контексту (знову марнення, але не можу підбрати слова - адже напряму воно в дереві компонентів не присутнє), де я намагаюсь його зчитувати, доки не викличеться ось це:

onChange: keys => {
            setSelectedRowKeys(keys);
            onFooterMenu([<FooterMenu key="footer" visible={keys.length > 0} onClickCopy={ copyClick } />]);
        }

Відбувається наступне, оновлюється вся таблиця, окрім тої віртуальної частини, яка породженна кліком на елементі з FooterMenu, а це сам обробник copyClick, обробник onDestinationSelect (і взагалі весь компонент DestinationModal, який ніде явно в дереві компонентів не розміщується), а також обробник doCopy, що викликається на натискання кнопки Ok. Все це вписується в одне дерево, яке буде оновлюватися лише після спрацювання onChange для вибор рядків таблиці.

Я так розумію цю проблему. Можливо, я щось і наплутав, але заміна виклику confirm() на компонент <Modal...> і тим самим вбудовування цього всього лайна в дерево компонента DirectoryTable має змінити ситуацію... Як спробую - відпишусь про результати.

Подякували: Yola1

10

Re: React: не працює state setter

Я виявився правий - коли переробив виклик confirm() на компонент. тобто на виклик setDestModalVisible(true), а сам компонент <DestinationModal> доопрацювавши помістив у рендер компонента <DirectoryTable>, то все відразу ж і запрацювало.
При цьому навіть сам стан destination тепер переліз повністю всередину компонента <DestinationModal>.

От доки не написав на форумі, ніяк не міг розковиряти проблему. В житті так в мене часто буває - є проблема і повний ступор, варто лише пожалітися комусь на проблему, вона вирішується сама, рішення приходить фактично у вигляді інсайту. А жалітися трохи соромно, от і сидиш б'єшся з проблемою наодинці :)

Можливо, допомагає стороння увага та інший кут погляду на проблему. Коротше, дякую Yola за участь.

Подякували: Yola1

11

Re: React: не працює state setter

setDestination(data);
console.log(destination);

така перевірка марна, тут console.log виведе ще старе значення. щоб подивитись на нове то console.log(destination); потрібно винести за межі цієї функції, наприклад перед return компонента.
А якщо треба відслідкувати зміну стейту то використати useEffect:

useEffect(() => console.log(destination), [destination])

12

Re: React: не працює state setter

Практика показує, що не завжди марна... іноді це спрацьовує. Але ж проблема була в іншому.