React Native + Redux: What's the best and pref

2019-07-18 17:46发布

I would like to use Redux for React Native. Currently I have no set data management, so the index.ios.js has the following:

  renderScene(route, navigator){
    this._navigator = navigator

    return (
        <route.component navigator={navigator} {...route.passProps}/>
    )
  }

<Navigator
  configureScene={this.configureScene}
  initialRoute={{name: 'Login', component: Login}}
  renderScene={(route, navigator) => this.renderScene(route, navigator)}
  style={styles.container}
  navigationBar={
    <Navigator.NavigationBar
      style={styles.navBar}
      routeMapper={NavigationBarRouteMapper}
    />
  }
/>

I am planning to switch to Redux. With my current set up, not using any frameworks, only exception being react-router, what would be the most preferred way to navigate using Redux? A sample using store, reducers, and actions would be extremely helpful.

2条回答
狗以群分
2楼-- · 2019-07-18 18:42

EDIT: react-navigation should be used now instead of NavigationExperimental.

--

I advise you use NavigationExperimental instead of Navigator. The last is depreciated. There is an example in the RN documentation, right here. It uses simple reducers to manage your navigation state. But you can also handle this state with Redux since it is the same logic.

This is my setup with tabs, using Redux and NavigationExperimental:

navigator.js (note: the renderOverlay prop in CardStack will be renamed renderHeader in 0.32)

import React from 'react';
import { NavigationExperimental } from 'react-native';
import { connect } from 'react-redux';
import { pop, push, selectTab } from './actions';

const {
  CardStack: NavigationCardStack,
} = NavigationExperimental;

class Navigator extends React.Component {
  constructor(props) {
    super(props);
    this._renderHeader = this._renderHeader.bind(this);
    this._renderScene = this._renderScene.bind(this);
  }

  _renderHeader(sceneProps) {
    return (
      <MyHeader
        {...sceneProps}
        navigate={this.props.navigate}
      />
    );
  }

  _renderScene(sceneProps) {
    return (
      <MyScene
        {...sceneProps}
        navigate={this.props.navigate}
      />
    );
  }

  render() {
    const { appNavigationState: { scenes, tabKey, tabs } } = this.props;

    return (
      <View style={{ flex: 1 }}>
        <NavigationCardStack
          key={`stack_${tabKey}`}
          navigationState={scenes}
          renderOverlay={this._renderHeader}
          renderScene={this._renderScene}
        />
        <Tabs
          navigationState={tabs}
          navigate={this.props.navigate}
        />
      </View>
    );
  }
}

const getCurrentNavigation = (navigation) => {
  const tabs = navigation.tabs;
  const tabKey = tabs.routes[tabs.index].key;
  const scenes = navigation[tabKey];
  return {
    scenes,
    tabKey,
    tabs,
  };
};

const mapStateToProps = (state) => {
  return {
    appNavigationState: getCurrentNavigation(state.navigation),
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    navigate: (action) => {
      if (action.type === 'pop') {
        dispatch(pop());
      } else if (action.type === 'push' && action.route) {
        dispatch(push(action.route));
      } else if (action.type === 'tab' && action.tabKey) {
        dispatch(selectTab(action.tabKey));
      }
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(Navigator);

actions.js

export function pop() {
  return ({
    type: 'POP_ROUTE',
  });
}

export function push(route) {
  return ({
    type: 'PUSH_ROUTE',
    route,
  });
}

export function selectTab(tabKey) {
  return ({
    type: 'SELECT_TAB',
    tabKey,
  });
}

reducer.js (note: I use immutable here, but it's up to you to use whatever fit you)

import { NavigationExperimental } from 'react-native';
import { Record } from 'immutable';

const {
  StateUtils: NavigationStateUtils,
} = NavigationExperimental;


export const InitialState = Record({
  // Tabs
  tabs: {
    index: 0,
    routes: [
      { key: 'tab1' },
      { key: 'tab2' },
      { key: 'tab3' },
    ],
  },
  // Scenes
  tab1: {
    index: 0,
    routes: [{ key: 'tab1' }],
  },
  tab2: {
    index: 0,
    routes: [{ key: 'tab2' }],
  },
  tab3: {
    index: 0,
    routes: [{ key: 'tab3' }],
  },
});
const initialState = new InitialState();


export default function navigation(state = initialState, action) {
  switch (action.type) {
    case 'POP_ROUTE': {
      const tabs = state.get('tabs');
      const tabKey = tabs.routes[tabs.index].key;
      const scenes = state.get(tabKey);
      const nextScenes = NavigationStateUtils.pop(scenes);

      if (scenes !== nextScenes) {
        return state.set(tabKey, nextScenes);
      }
      return state;
    }

    case 'PUSH_ROUTE': {
      const route = action.route;
      const tabs = state.get('tabs');
      const tabKey = tabs.routes[tabs.index].key;
      const scenes = state.get(tabKey);
      const nextScenes = NavigationStateUtils.push(scenes, route);

      if (scenes !== nextScenes) {
        return state.set(tabKey, nextScenes);
      }
      return state;
    }

    case 'SELECT_TAB': {
      const tabKey = action.tabKey;
      const tabs = state.get('tabs');
      const nextTabs = NavigationStateUtils.jumpTo(tabs, tabKey);

      if (nextTabs !== tabs) {
        return state.set('tabs', nextTabs);
      }
      return state;
    }

    default:
      return state;
  }
}

Finally, in MyScene.js component you can simply handle the component to show by testing the current route (with a switch for example). By passing the navigate function in your components, you can manage your scenes (ie: this.props.navigate({ type: 'push', route: { key: 'search' } })).

Notice that you can handle your state and your actions as you wish. The main idea to understand is how the state is reduced, regardless you use redux or not.

查看更多
Root(大扎)
3楼-- · 2019-07-18 18:43

I suggest you use react-navigation for native and web environment it will replace NavigationExperimental just take a look the following example.

  • Components built for iOS and Android
  • Share navigation logic between mobile apps, web apps, and server rendering.
  • Documentation is pretty straightforward
  • Develop your own navigation views
  • It is easy!

Just define your navigation in your app.

import {
  TabNavigator,
} from 'react-navigation';

const BasicApp = TabNavigator({
  Main: {screen: MainScreen},
  Setup: {screen: SetupScreen},
});

...and navigate

class MainScreen extends React.Component {
  static navigationOptions = {
    label: 'Home',
  };
  render() {
    const { navigate } = this.props.navigation;
    return (
      <Button
        title="Go to Setup Tab"
        onPress={() => navigate('Setup')}
      />
    );
  }
}

And do the following in order to add redux just define your reducer...

const AppNavigator = StackNavigator(AppRouteConfigs);

const appReducer = combineReducers({
  nav: (state, action) => (
    AppNavigator.router.getStateForAction(action, state)
  )
});

...and navigate in your component with addNavigationHelpers

  <AppNavigator navigation={addNavigationHelpers({
    dispatch: this.props.dispatch,
    state: this.props.nav,
  })} />
查看更多
登录 后发表回答