1
votes

I've created a React Native app with navigation provided by react-navigation, also integrating redux with react-navigation-redux-helpers, and I'm trying to figure out a good way of implementing a globally-available 'SignOutHeaderButton' component that, when pressed, will dispatch a redux action and perform a navigation operation.

At the moment, I'm having to pass a function via screenProps from the application root component, which is the function that dispatches the redux action. This function is then passed to the UpdatesListView container component via screenProps, and is then passed into the SignOutHeaderButton common component as a prop via navigationOptions.

Is there a better way in which I can implement this so that I don't have to pass any props into the SignOutHeaderButton component and without having to instantiate a signOut() function within each container component in the application from which there will be a 'Sign Out' button?

App.js:

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { reduxifyNavigator } from 'react-navigation-redux-helpers';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider, connect } from 'react-redux';

import { appContainer } from './src/navigators';
import { store, persistor } from './src/store/configureStore';
import { signOut } from './src/actions/auth';

const app = reduxifyNavigator(appContainer, 'root');

const mapStateToProps = state => {
  return {
        state: state.navReducer
  }
}

const AppWithNavigationState = connect(mapStateToProps)(app);

export default class App extends React.Component {

  signOut() {
    store.dispatch(signOut());
  }

  render() {
    return (
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          <AppWithNavigationState
            screenProps={{
              signOut: this.signOut
                }} />
        </PersistGate>
      </Provider>
    )
  }
}

UpdatesListView.js:

import React from 'react';
import { View, Container, Content, Text, Button } from 'native-base';
import { connect } from 'react-redux';

import SignOutHeaderButton from '../components/common/SignOutHeaderButton';

class UpdatesListView extends React.Component {

  constructor(props) {
    super(props);
  }

  static navigationOptions = ({ navigation }) => {
    return {
      headerTitle: 'Updates',
      headerRight: <SignOutHeaderButton signOut={navigation.getParam('signOut')} />
    }
  }

  componentDidMount() {
    this.props.navigation.setParams({
      signOut: this.props.screenProps.signOut
    })
  }

  render() {
    return (
      <Container>
        <Text>UpdatesListView</Text>
      </Container>
    )
  }
}


const mapStatetoProps = state => {
  return {
        updates: state.updatesReducer,
        tags: state.tagsReducer
  }
}

export default connect(mapStateToProps)(UpdatesListView);

SignOutHeaderButton.js:

import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';

class SignOutHeaderButton extends React.Component {

  constructor(props) {
    super(props);
  }

  signOut() {
    this.props.signOut();
    this.props.navigation.navigate('AuthStack');
  }

  render() {
    return (
      <Button
        title="Sign Out"
        onPress={this.signOut} />
    )
  }
}

export default withNavigation(SignOutHeaderButton);
1

1 Answers

2
votes

Use redux connect, the idea of redux connect is to bind the store dispatch state to your component this, When you use redux connect you can access any redux store dispatch and state from anywhere in your application this works for both react and react-native, for example for SignOutHeaderButton.js:

import React from 'react';
import { Button } from 'react-native';
import { connect } from 'react-redux';
import { withNavigation } from 'react-navigation';

class SignOutHeaderButton extends React.Component {

  constructor(props) {
    super(props);
  }

  signOut() {
    this.dispatch(signOut());
    this.props.navigation.navigate('AuthStack');
  }

  render() {
    return (
      <Button
        title="Sign Out"
        onPress={this.signOut} />
    )
  }
}

export default connect()(withNavigation(SignOutHeaderButton));

Also, you can pass the redux store state to your component by passing a function to your connect(/*your function*/) to resolve the state data you want. for better understanding, you can try this tutorial: https://blog.logrocket.com/react-redux-connect-when-and-how-to-use-it-f2a1edab2013

Note using nested smart components is very common, redux connect has a very smart algorithm to compare the store state changes, please check this: https://hackernoon.com/why-using-nested-connect-react-redux-components-is-good-bd17997b53d2 enter image description here