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;
Другие вопросы по тегам