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.