没有 redux 的 React 导航状态管理

React navigation state management without redux

我正在为我的 React Native 项目使用 React Navigation 库,并且努力了解如何使用它处理状态。 在普通的 React Native 应用程序中,我可以在顶级组件中拥有状态,并通过道具从子组件弹出事件,但是对于 React Navigation,我似乎无法将任何道具传递给用作屏幕的组件。

通读 related GitHub issue 后,我认为库开发人员似乎非常固执己见,强迫每个人使用某种全局事件处理程序 - 我猜是 redux 或 mobx。

The handler which needs to modify the following state。当我开始尝试在应用程序内移动状态时,我卡住了,因为我不知道如何:

  1. 将处理程序传递给 TaskForm 组件。
  2. 如果它作为 App.js
  3. 的一部分呈现,则将状态作为道具传递给 TaskList

请避免回复 "just use redux"。我相信在这个例子中使用 redux 会大材小用。

我在没有 redux 的情况下在我的应用程序中使用 react native 和 react navigation,到目前为止它运行良好。诀窍是一直传递 screenProps

例如,我有更多视图。我在堆栈中创建了一个包含 2 个子视图的基本更多视图:

class More extends Component {
    render() {
        return <something>
    }
}

class SubView1 extends Component {...}
class SubView2 extends Component {...}

然后我创建堆栈:

const MoreStack = StackNavigator({
More: {
    screen: More
}, 
SubView1: {
    screen: SubView1,
},
...
}, options);

然后我创建一个包装器 class 我导出:

export default class MoreExport extends Component {
    static navigationOptions = {
        title: "More"
    }

    render() {
        return <MoreStack screenProps={this.props.screenProps} />;
    }
}

如果所有这些都在 More.js 中,我可以从 More.js 中导入更多并在其他任何地方使用它。

如果我然后将 screenProps 传递到我的根视图,然后为每个层都有一个包装器 class,我可以一直向下传递 screenProps,并且所有视图都可以使用 this.props.screenProps.

我在每个 StackNavigator 和 TabNavigator 周围都使用了类似上面的包装器,并且 screenProps 一直向下传递。

例如,在我的根 class 的渲染方法中,我可以这样做:

return <More screenProps={{prop1: something, prop2: somethingElse}} />

然后 More class 和 MoreStack 中的每个 SubView 都可以访问这些道具。

如果您需要更多说明,请随时告诉我!

免责声明:我不知道这是正确的还是推荐的方法,但它确实有效

在受到 steffeydev 的启发后,我更多地关注了 React 社区问题,发现了一个非常简单的包装器使用函数的示例。

很意外simple solution也不知道为什么之前没想到。 函数如下: const createComponent = (instance, props) => navProps => React.createElement(instance, Object.assign({}, props, navProps));

感谢您的启发,并向我指出 screenProps,这使我找到了这个解决方案。

您可以像这样设置导航参数:

static navigationOptions = ({ navigation }) => {
    return {
        tabBarIcon: ({ tintColor, focused }) =>
            <View>
                <Icon name="bell-ring" size={24} color={focused ? 'green' : 'black'} />
                {(navigation.state.params.badgeCount && navigation.state.params.badgeCount > 0) ?
                    <Text>{navigation.state.params.badgeCount}</Text>
                    :
                    <View></View>
                }
            </View>
    }
}

并将 badgeCount 更改为:

this.props.navigation.setParams({ badgeCount: 3 })

在发现 React Navigation 文档的以下部分之前,我一直在努力解决同样的问题并尝试了其他一些答案:https://reactnavigation.org/docs/en/stack-navigator.html#params.

本质上,Stack 中的每个 Screen 都可以传递 params,其中可以包含处理程序,然后各种屏幕可以与应用程序状态进行交互。

然后我的一般结构是有一个带有状态和处理程序的 App class,然后根据需要将处理程序传递到每个导航 Screen。我不确定这个模式是否正确,但这是我理解一般 React 教程的方式。

示例:在我的演示应用程序中,我的页面流如下:

  • 公园查找器屏幕 -> 公园详细信息屏幕(带有 Bookmark 操作)
  • 书签列表屏幕 -> 公园详情屏幕

如果找到公园,可以单击 Bookmark 按钮,将公园添加到书签屏幕上显示的书签列表中。然后您可以单击公园书签查看详细信息。

我的 App 大致是这样的:

import React, { Component } from 'react';

// Screens
import ParkFinderScreen from './Components/ParkFinderScreen';
import ParkBookmarksScreen from './Components/ParkBookmarksScreen';
import ParkDetailsScreen from './Components/ParkDetailsScreen';

// Navigation
import { createStackNavigator, createBottomTabNavigator, createAppContainer } from 'react-navigation';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      bookmarks: new Map()
    };
  }

  // Bookmark the park shown in the detail section.
  handleBookmark (park) {
    let newBookmarks = this.state.bookmarks;

    newBookmarks.set(park.parkCode, park);

    this.setState({
      bookmarks: newBookmarks
    });
  }

  render() {
    const FinderStack = createStackNavigator(
      {
        ParkFinder: {
          screen: ParkFinderScreen
        },
        ParkFinderDetails: {
          screen: ParkDetailsScreen,
          params: {
            handleBookmark: (park) => this.handleBookmark(park),
          }
        },
      }
    );

    const BookmarksStack = createStackNavigator(
      {
        ParkBookmarks: {
          screen: ParkBookmarksScreen,
          params: {
            bookmarks: this.state.bookmarks
          }
        },
        ParkBookmarksDetails: {
          screen: ParkDetailsScreen,
        },
      }
    );

    const AppNavigator = createBottomTabNavigator(
      {
        Bookmarks: BookmarksStack,
        Finder: FinderStack,
      }
    );

    const AppContainer = createAppContainer(AppNavigator);

    return (
      <AppContainer/>
    );
  }
}

export default App;

我正在使用 Apollo Client,但我删除了那些部分。

Screen 组件中,您可以使用 this.props.navigation.getParam('bookmarks').

像访问其他组件一样访问 props

我遇到的一个问题是,每当我更改应用程序状态时,我都会被带到第一个屏幕。状态已更新,但有点迷失方向。我不确定是否有办法在停留在屏幕上的同时更新 App 状态。我可以理解,鉴于 App 状态已更新,所有子项都需要更新,因此当前屏幕(我认为是子项的一部分)将被重置。我不知道这是系统的限制还是我设计组件的副产品。

我希望这对某人有所帮助。这似乎符合 React Native 的预期行为。库团队似乎真的希望你不要使用 Redux。 https://reactnavigation.org/docs/en/redux-integration.html