Асинхронные действия требуют времени. Теоретически могло пройти неопределенное количество времени. Таким образом, нет никакого способа гарантировать, что ваши данные будут извлечены перед монтированием вашего компонента.
Вместо этого вам нужно действовать так, как если бы данные могли быть неопределенными и иметь состояние приложения, которое их обрабатывает.
Что касается получения доступа к переменной страниц сразу после вызова setPages
, это также не удастся, потому что React фактически выполняет повторный рендеринг асинхронно.
Даже если он выполнялся одновременно, есть вещь под названием closures
, в которой javascript извлекает все переменные вокруг функции при ее создании, так что функция всегда имеет доступ к ним. React Hooks работают за счет использования этих замыканий, чтобы иметь доступ только к переменным, какими они были, когда компонент был визуализирован / повторно визуализирован. Причина этого в том, что при каждом повторном рендеринге все функции в вашем компоненте воссоздаются, что создает новое закрытие.
Итак, это означает, что вам нужно сохранить эти концепции помните, когда вы работаете с React.
Что касается вашего кода, решение для результатов, которое настроил Dlucidione, является одним из ваших лучших вариантов, помимо настройки отдельного useEffect для обновления page
при изменении pages
.
const history = useHistory(); //from react-router-dom
useEffect(() => {
async function fetchData() {
return await fetch(`https://myBackend/pages`)
.then((response) => response.json())
.then((response) => {
setPages(response);
})
.catch((err) => setErrors(err));
}
fetchData();
return () => {
console.log('unmounting...');
};
}, []);
useEffect(() => {
const url = props.match.params.url;
if (!pages || !url) return;
// Using Array#find here because I assume based on the name, you want one page rather than an array
const page = pages.find((singlepage) => {
return singlepage.url === url;
});
if (!page) {
// This will change the route if a page isn't found.
history.push('/404');
}
setPage(page);
}, [pages, props.match.params.url, history]);
Redirect
и history.push
работают по-разному. Redirect
- это декларативный метод навигации, а history.push
- это программный c.
Пример использования history.push
:
if (!page) {
// This will change the route if a page isn't found.
history.push('/404');
}
Важное примечание, это можно использовать где угодно в коде, если вы можете передать туда объект истории. Я и раньше использовал его в преобразователях redux.
Пример использования Redirect
без перенаправления на mount:
if(!pages && !page){
return <Redirect to="/404"/>
}
Или в каком-то JSX:
<div>
{!pages && !page && <Redirect to="/404"/>}
</div>
Redirect имеет для рендеринга, что означает, что его можно использовать только в операторе return компонента.
Как я понимаю: перенаправление основано на том, найдена ли отдельная страница в процессе монтирования, поэтому процесс перенаправления также должен быть в процессе монтирования, чтобы проверить его перед рендерингом чего-либо перенаправлять или нет.
Это правильно. Когда Redirect монтируется или обновляется, он будет перенаправлять, если условия верны. Если в перенаправлении есть свойство path
или from
(они являются псевдонимами друг друга), то это ограничивает работу перенаправления только при совпадении этого пути. Если путь не совпадает, перенаправление ничего не сделает, пока путь не будет изменен на соответствующий (или, конечно, он не отключится).
Под капотом , перенаправление просто вызывает history.push
или history.replace
в componentDidMount и componentDidUpdate (через компонент Lifecycle), так что принимайте это как хотите.