未声明的基于商店的 React Navigation 导致警告

Unstated store based React Navigation causing warning

我在我的 React Native 项目中使用了 React-Navigation 和 Unstated。

我有一种情况想使用:

this.props.navigation.navigate("App")

登录成功后

问题是我不希望它直接从分配给提交按钮的函数完成。我想根据全球 Unstated 商店进行导航。

但是,这意味着我需要使用 Subscribe 包装器的条件 INSIDE。这就是导致可怕的 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>
    );

有效。但正确的做法是什么? 我无法在 Subscribe 之外访问 MainStore,因此在 render.

之外

我不确定反应导航模式,但您可以在订阅 '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;

在这个 Header 示例中这样使用:

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;