Реагировать / повторно выбрать, как обновить sh пользовательский интерфейс, прежде чем пересчитать селектор - PullRequest
0 голосов
/ 19 марта 2020

В моем React-App (create-реагировать-приложение) я использую селектор (созданный с помощью повторного выбора) для вычисления производных данных из сохраненных данных. Проблема в том, что селектор занимает много времени для вычисления. Я хотел бы показать спиннер (или сообщение) в пользовательском интерфейсе. Но каждый раз, когда селектор пересчитывается, пользовательский интерфейс зависает.

Я читаю много вещей (Web Worker, requestIdleCallback, requestAnimationFrame) и пытаюсь создать свой собственный React-хук, но я застрял. Я не могу использовать селектор в обратных вызовах.

В результате поиска просто обновляется пользовательский интерфейс перед повторным вычислением селектора.

1 Ответ

0 голосов
/ 19 марта 2020

Это мое решение. Я не знаю, «хорошо» ли это или нарушает ли некоторые правила реагирования или повторного выбора, но это работает. Может быть, вы можете оценить это? Код упрощен для улучшения читабельности.

Идея состоит в том, что селектор возвращает Promise, и я вызываю requestAnimationFrame перед вычислением данных, чтобы получить возможность обновить sh пользовательский интерфейс.

селектор. js:

const dataSelector = state => state.data;

export const getDataComputedPromise = createSelector([dataSelector], (data) => {
    const compute = function () {
        return new Promise((resolve) => {
            // heavy computing stuff
            resolve(computedData);
        });
    };

    return new Promise((resolve) => {
        let start = null;
        let requestId = null;
        function step (timestamp) {
            if (!start) {
                start = timestamp;
                window.cancelAnimationFrame(requestId);
                requestId = window.requestAnimationFrame(step);
                return;
            };
            compute().then(freeResources => {
                window.cancelAnimationFrame(requestId);
                resolve(freeResources);
            });
        }
        requestId = window.requestAnimationFrame(step);
    });
});

myPage. js

const MyPage = ({ someProps }) => {
    ...
    const dataComputedPromise = useSelector(getDataComputedPromise);
    const [dataComputed, setDataComputed] = useState([]);
    const [computeSelector, setComputeSelector] = useState(false);
    useEffect(() => {
        setComputeSelector(true);
    }, [data]);
    useEffect(() => {
        dataComputedPromise.then(dataComputed => {
            setDataComputed(dataComputed);
            setComputeSelector(false);
        });
    }, [dataComputedPromise]);
    ...
    return <div>
        <Loader active={compueSelector}>Computing data...</Loader>
    </div>;
};

export default MyPage;
...