React Navigation на основе неустановленного магазина вызывает предупреждение
Я использую реагирующую навигацию и Unstated в своем собственном проекте реакции.
У меня есть ситуация, где я хотел бы использовать:
this.props.navigation.navigate("App")
после успешного входа.
Проблема в том, что я не хочу, чтобы это делалось напрямую из функции, назначенной кнопке отправки. Я хочу ориентироваться на основе глобального магазина Unstated.
Тем не менее, это означает, что мне нужно будет использовать условно ВНУТРИ Subscribe
обертка. Вот что приводит к страшным Warning: Cannot update during an existing state transition (such as within 'render').
render() {
const { username, password } = this.state;
return (
<Subscribe to={[MainStore]}>
{({ auth: { state, testLogin } }) => {
if (state.isAuthenticated) {
this.props.navigation.navigate("App");
return null;
}
console.log("rendering AuthScreen");
return (
<View style={styles.container}>
<TextInput
label="Username"
onChangeText={this.setUsername}
value={username}
style={styles.input}
/>
<TextInput
label="Password"
onChangeText={this.setPassword}
value={password}
style={styles.input}
/>
{state.error && (
<Text style={styles.error}>{state.error.message}</Text>
)}
<Button
onPress={() => testLogin({ username, password })}
color="#000"
style={styles.button}
>
Sign in!
</Button>
</View>
);
}}
</Subscribe>
);
Оно работает. Но как правильно это сделать? У меня нет доступа к MainStore
вне Subscribe
и, следовательно, вне render
,
1 ответ
Я не уверен насчет шаблонов реагирования-навигации, но вы можете использовать обертку вокруг этого компонента, которая подписывается на "MainStore", и передавать ее этому компоненту в качестве реквизита. Таким образом, вы получите доступ к MainStore вне метода рендеринга.
С тех пор я нашел лучшее решение. Я создал HOC, который я сейчас вызываю на любом Компоненте, функциональном или нет, который требует доступа к магазину. Это дает мне доступ к состоянию магазина и все функции в реквизитах. Это означает, что я могу свободно использовать компонент, как это было задумано, зацепки и все.
Вот как это выглядит:
WithUnstated.js
import React, { PureComponent } from "react";
import { Subscribe } from "unstated";
import MainStore from "../store/Main";
const withUnstated = (
WrappedComponent,
Stores = [MainStore],
navigationOptions
) =>
class extends PureComponent {
static navigationOptions = navigationOptions;
render() {
return (
<Subscribe to={Stores}>
{(...stores) => {
const allStores = stores.reduce(
// { ...v } to force the WrappedComponent to rerender
(acc, v) => ({ ...acc, [v.displayName]: { ...v } }),
{}
);
return <WrappedComponent {...allStores} {...this.props} />;
}}
</Subscribe>
);
}
};
export default withUnstated;
Используется как в этом примере заголовка:
import React from "react";
import { Text, View } from "react-native";
import styles from "./styles";
import { states } from "../../services/data";
import withUnstated from "../../components/WithUnstated";
import MainStore from "../../store/Main";
const Header = ({
MainStore: {
state: { vehicle }
}
}) => (
<View style={styles.plateInfo}>
<Text style={styles.plateTop}>{vehicle.plate}</Text>
<Text style={styles.plateBottom}>{states[vehicle.state]}</Text>
</View>
);
export default withUnstated(Header, [MainStore]);
Так что теперь вам не нужно создавать миллион компонентов-оболочек за все время, пока ваш магазин будет доступен вне функции рендеринга. Как еще один положительный герой, HOC принимает множество магазинов, что делает его полностью готовым к работе. И - это работает с вашими опциями навигации!
Просто не забудьте добавить displayName в свои магазины (ES-Lint предложит вам в любом случае).
Вот как выглядит простой магазин:
import { Container } from "unstated";
class NotificationStore extends Container {
state = {
notifications: [],
showNotifications: false
};
displayName = "NotificationStore";
setState = payload => {
console.log("notification store payload: ", payload);
super.setState(payload);
};
setStateProps = payload => this.setState(payload);
}
export default NotificationStore;