7
votes

The issue:

I have tried to use redux in combination with the routing provided by react-native-router-flux.

In simple words, it is not working:

  • redux state modifications will not appear in view, but can be logged successfully to console
  • every time an action is taken, the whole component tree with the scenes will be recreated, which results in lots of warnings in console, that a scene with the key "xyz" was already created.

What i did:

I used the official example application of react-native-router-flux and added a simple counter-example based on redux:

  • a simple state: "counter" (might be an integer)
  • reducers and actions (increment, decrement)

Below i will show some code, but also one will find my example application on github, please feel free to check it out: https://github.com/itinance/react-native-router-flux. The "Example" Directory is the original example application. And "ExampleRedux" is a copy of the example with a redux stack and example reducers (a counter), what i am talking about here.

The main application component with provider and store:

import * as reducers from './components/reducers';

const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);

const store = createStoreWithMiddleware(reducer);

export default class Example extends React.Component {
    render() {
        return (
            <Provider store={store}>
                <ExampleContainer/>
          </Provider>
        );
    }
}

My reducers:

const initialState = {
  count: 0
};

export default function counter(state = initialState, action = {}) {

  switch (action.type) {
      case "INCREMENT":
        console.log("do increment", state,
            {
                ...state,
                count: state.count + 1
            }
        );

      return {
        ...state,
        count: state.count + 1
      };
    case "DECREMENT":
      return {
        ...state,
        count: state.count - 1
      };
    default:
      return state;
  }
}

The inner application container for redux' "connect":

I pass the state and actions to the scene component:

<Scene key="launch" component={Launch} title="Launch" initial={true} state={state} {...actions} />

The whole code of this ExampleContainer:

class ExampleContainer extends React.Component {

    constructor(props) {
        super(props);
    }

    render() {
        const { state, actions } = this.props;
        console.log("Props", this.props, state, actions); // everything ok here

        return <Router createReducer={reducerCreate}>
            <Scene key="modal" component={Modal} >
                <Scene key="root" hideNavBar={true}>
                    <Scene key="echo" clone component={EchoView} />
                    <Scene key="register" component={Register} title="Register"/>
                    <Scene key="home" component={Home} title="Replace" type="replace"/>
                    <Scene key="launch" component={Launch} title="Launch" initial={true} state={state} {...actions} />
                    .... lots of other scenes ...
                    </Scene>
                </Scene>
                <Scene key="error" component={Error}/>
            </Scene>
        </Router>;
    }
}


export default connect(state => ({
   state: state.counter
 }),
 (dispatch) => ({
   actions: bindActionCreators(actions, dispatch)
 })
)(ExampleContainer);

And this is the dead easy LaunchScreen having simple counter functionality (and example routes for demonstrating the routing):

class Launch extends React.Component {

    constructor(props) {
        super(props);
        console.log(props);
    }

    render(){
        const { state, increment, decrement } = this.props;

        console.log("Props 2: ", this.props, state, increment, decrement);

        return (
            <View {...this.props}  style={styles.container}>
                <Text>Launch page</Text>

                <Text>{state.count}</Text>

                <Button onPress={increment}>Increment</Button>

                <Button onPress={()=>Actions.login({data:"Custom data", title:"Custom title" })}>Go to Login page</Button>
                <Button onPress={Actions.register}>Go to Register page</Button>
                <Button onPress={Actions.register2}>Go to Register page without animation</Button>
                <Button onPress={()=>Actions.error("Error message")}>Popup error</Button>
                <Button onPress={Actions.tabbar}>Go to TabBar page</Button>
               <Button onPress={Actions.pop}>back</Button>
            </View>
        );
    }
}

The actions are perfectly dispatched when clicking on Increment-Button. In the console the new state can be logged successfully. It increments one by one with every click. But not in the view.

When i ommit the router and scenes and only activate the launch screen as a single component, everything works fine.

Question:

How to make this redux application work, that views will update their state correctly?

For the sake of completeness, the whole code can be found on github.

1
FYI - There is no ExampleRedux folder on the master branch of the fork you provided. Did you put it on a different branch? I'm interested in looking at the code.Javid Jamae

1 Answers

6
votes

You have probably forgotten to connect the Launch component to the store. What you want to do is similar to what you have done in ExampleContainer, i.e.

export default connect(state => ({
   state: state.counter
 }),
 (dispatch) => ({
   actions: bindActionCreators(actions, dispatch)
 })
)(Launch);

and then the correct values will show up in your log