Actions not being passed to redux in react-native-router-flux - react-native

I followed the instructions set out in https://github.com/aksonov/react-native-router-flux/blob/master/docs/v3/REDUX_FLUX.md#step-1 to a tee in version beta.24 and when I navigate via Action.push, pop, replace, etc there is no corresponding action that is passed through to my reducer.
i print at the top of my reducer and can capture events I pass through dispatch manually. Are there common issues that I could run into?
Code
Reducer
import { ActionConst } from 'react-native-router-flux';
const initialState = {
scene: {},
};
export default function SceneReducer(state = initialState, action) {
console.log(action);
switch (action.type) {
case ActionConst.FOCUS:
return { ...state, scene: action.scene };
default:
return state;
}
}
Combined Reducers
import { combineReducers } from 'redux';
import SceneReducer from './SceneReducer';
const rootReducer = combineReducers({
routing: SceneReducer,
// other reducer here...
});
export default rootReducer;
App
import RootReducer from './RootReducer';
import loginRouter from './LoginRouter';
const ReduxRouter = connect()(Router);
const store = compose(applyMiddleware(thunkMiddleware))(createStore)(RootReducer);
const navigator = Actions.create(
<Modal hideNavBar>
<Scene key="root" hideNavBar>
<Scene key='login1' component{Log1} />
<Scene key='login2' component{Log2} />
</Scene>
<Scene key="modalRoot"><Scene key="modal" component={Comp} /></Scene>
</Modal>,
);
export default class AppRouter extends Component {
render() {
return (
<Provider store={store}>
<ReduxRouter scenes={navigator} />
</Provider>
);
}
}
Thanks for the help!

Try replace your ReduxRouter with this:
import { Router, Reducer } from 'react-native-router-flux';
import { connect } from 'react-redux';
const ReduxRouter = connect()(({ dispatch, children, ...props }) => (
<Router
{...props}
createReducer={params => (state, action) => {
dispatch(action);
return Reducer(params)(state, action);
}}
>
{children}
</Router>
));
Also, for the reducer, the action's route key is routeName rather than scene (maybe your version differs so look out for both):
I'm using "react-native-router-flux": "4.0.0-beta.27".

There are some of the modifications that you need to do in your code.
You need to implement and use reducer object from react-native-router-flux, which defines and handles the actions appropriately.
Then bind it to your SceneReducers.js as
import {Reducer, ActionConst, Actions} from 'react-native-router-flux'
const defaultReducer = Reducer();
export default (state, action) => {
console.log(action);
switch (action.type) {
case ActionConst.FOCUS:
return defaultReducer(state, action);
default:
return defaultReducer(state, action);
}
}
It is important to load reducers AFTER actions.create, so don't use import here.
This is because the initial state of the reducer' must be available at compile time and Router is created runtime.
Therefore in your App use
// create actions
// connect your router to the state here
const ReduxRouter = connect((state) => ({ state: state.routing }))(Router);
const RootReducer = require('./RootReducer').default;
// define the store
const store = compose(applyMiddleware(thunkMiddleware))(createStore)(RootReducer);
Have a look at this thread to follow-up.You might run into issues due to non-deep linking of the external states, therefore you can check it on version until 4.0.0-beta.23 as mentioned in this comment.
Hope it helps

Related

Use redux action the dispatch is not working

I have combined my react redux.
Here is my App.js
import React from 'react';
import ReduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { compose, createStore, applyMiddleware } from 'redux';
import reducers from './src/reducers';
import AppContainer from './src/navigator'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const App: () => React$Node = () => {
const store = createStore(reducers, {}, composeEnhancers(applyMiddleware(ReduxThunk)));
return (
<Provider store={store}>
<AppContainer />
</Provider>
);
};
export default App;
src/reducers/index.js
import { combineReducers } from 'redux';
import LoginReducer from './LoginReducer';
export default combineReducers({
LoginRedux: LoginReducer
});
If I use my action login(), I can see login action start, but I can't see dispatch start
import React from 'react';
import {
Text,
View,
TouchableOpacity,
} from 'react-native';
import { connect } from 'react-redux';
import { login } from '../actions';
const LoginScreen = ({ navigation }) => {
// console.log('see my test value', testValue)
return (
<View>
<TouchableOpacity
onPress={() => {
login();
}
}>
<View>
<Text>LOGIN</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
const mapStateToProps = (state) => {
const { testValue } = state.LoginRedux;
console.log('mapStateToProps testValue =>', testValue);
return { testValue };
};
export default connect(mapStateToProps, { login })(LoginScreen);
If I console.log(dispatch), it will show dispatch is not defined.
import { LOGIN } from './types';
export const login = () => {
console.log('login action start')
return (dispatch) => {
console.log('dispatch start');
// console.log(dispatch);
dispatch({ type: LOGIN, testValue: 'I am test' });
};
};
src/reducers/LoginReducer.js
import { LOGIN } from '../actions/types';
const INITIAL_STATE = {
testValue: ''
};
export default (state = INITIAL_STATE, action) => {
console.log('reducer =>', action); // I can't see the console.log
switch (action.type) {
case LOGIN:
return {
...state,
testValue: action.testValue
};
default:
return state;
}
};
I have no idea why my action dispatch is not working. Do I set something wrong ?
Any help would be appreciated.
According to Zaki Obeid help, I update like this:
the action code:
export const login = () => {
console.log('login !');
return { type: LOGIN };
};
the function component code:
import { login } from '../../actions';
export const SettingScreen = ({ navigation, login }) => {
// return view code
}
const mapDispatchToProps = dispatch => ({
// you will use this to pass it to the props of your component
login: () => dispatch(login),
});
connect(null, mapDispatchToProps)(SettingScreen);
In LoginScreen component
you will need to add mapDispatchToProps
const mapDispatchToProps = dispatch => ({
// you will use this to pass it to the props of your component
login: () => dispatch(login()),
});
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
Then
you will need to destructure from the props as:
const LoginScreen = ({ navigation, login }) => {
// your code
}
In actions.js
the way you use dispatch here requires a library redux-thunk and it's used for async calls.
and the normal action should do the job for you:
export const login = () => ({
type: LOGIN,
testValue: 'I am test'
})
I hope this is useful and will solve your problem,
Have a good day.
In a react-redux app, you obtain the dispatch function either from getting a hold of the store object directly (store.dispatch), or via the react-redux connect function, which will provide dispatch as an argument to a function you write and then later hook up to a component
import { connect } from 'react-redux';
const mapStateToProps = ...
const mapDispatchToProps = (dispatch) => {
return {
someHandle: () => dispatch(myActionCreator())
}
}
export const connect(mapStateToProps, mapDispatchToProps)(MyComponent)
You can't just call dispatch out of thin air -- it's not a global function.
It seems you are using the login function directly. you will have to use the props. Just change the name for confusing and use through props.
import { combineReducers } from 'redux';
import LoginReducer from './LoginReducer';
export default combineReducers({
LoginRedux: LoginReducer
});
If I use my action login(), I can see login action start, but I can't see dispatch start
import React from 'react';
import {
Text,
View,
TouchableOpacity,
} from 'react-native';
import { connect } from 'react-redux';
import { login } from '../actions';
const LoginScreen = ({ navigation, userLogin }) => {
// console.log('see my test value', testValue)
return (
<View>
<TouchableOpacity
onPress={() => {
userLogin();
}
}>
<View>
<Text>LOGIN</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
const mapStateToProps = (state) => {
const { testValue } = state.LoginRedux;
console.log('mapStateToProps testValue =>', testValue);
return { testValue };
};
export default connect(mapStateToProps, { userLogin:login })(LoginScreen);

Cannot listen for a key that isn't associated with a Redux Store - React Navigtion

I just upgraded my React Navigation to version 1.0.0. They have new ways to integrate the navigation and Redux. Here's my code
configureStore.js
export default (rootReducer, rootSaga) => {
const middleware = []
const enhancers = []
/* ------------- Analytics Middleware ------------- */
middleware.push(ScreenTracking)
const sagaMiddleware = createSagaMiddleware({ sagaMonitor })
middleware.push(sagaMiddleware)
const navMiddleware = createReactNavigationReduxMiddleware('root', state => state.nav)
middleware.push(navMiddleware)
/* ------------- Assemble Middleware ------------- */
enhancers.push(applyMiddleware(...middleware))
/* ------------- AutoRehydrate Enhancer ------------- */
// add the autoRehydrate enhancer
if (ReduxPersist.active) {
enhancers.push(autoRehydrate())
}
const store = createAppropriateStore(rootReducer, compose(...enhancers))
// kick off root saga
sagaMiddleware.run(rootSaga)
return store
}
ReduxNavigation.js
const addListener = createReduxBoundAddListener('root')
// here is our redux-aware our smart component
function ReduxNavigation (props) {
const { dispatch, nav } = props
const navigation = ReactNavigation.addNavigationHelpers({
dispatch,
state: nav,
uriPrefix: prefix,
addListener
})
return <AppNavigation navigation={navigation} />
}
const mapStateToProps = state => ({ nav: state.nav })
export default connect(mapStateToProps)(ReduxNavigation)
ReduxIndex.js
export default () => {
/* ------------- Assemble The Reducers ------------- */
const rootReducer = combineReducers({
//few reducers
})
return configureStore(rootReducer, rootSaga)
}
App.js
const store = createStore()
class App extends Component {
render () {
console.disableYellowBox = true
return (
<Provider store={store}>
<RootContainer />
</Provider>
)
}
}
export default App
And I got an error of
Cannot listen for a key that isn't associated with a Redux store. First call createReactNavigationReduxMiddleware so that we know when to trigger your listener
I hope someone can help me and please let me know if you needed more information
Thanks
It is clearly mentioned in the react-navigation docs that the Note: createReactNavigationReduxMiddleware must be run before createReduxBoundAddListener.
Whenever you do use the module after importing it, the listener is being called before the store is initialized.
So the simple fix is put the addListener in the ReduxNavigation function as
// here is our redux-aware our smart component
function ReduxNavigation (props) {
const addListener = createReduxBoundAddListener('root')
const { dispatch, nav } = props
const navigation = ReactNavigation.addNavigationHelpers({
dispatch,
state: nav,
uriPrefix: prefix,
addListener
})
return <AppNavigation navigation={navigation} />
}
const mapStateToProps = state => ({ nav: state.nav })
export default connect(mapStateToProps)(ReduxNavigation)
or you may make a wrapper class to the current class and bind the store to it as here
class RootContainer extends Component {
render () {
return (
<View style={{flex: 1, backgroundColor: '#fff'}}>
<StatusBar translucent barStyle='dark-content' backgroundColor='#fff' />
<ReduxNavigation/>
</View>
)
}
}
class App extends Component {
render () {
console.disableYellowBox = true
return (
<Provider store={store}>
<RootContainer />
</Provider>
)
}
}
I have made a sample starter kit for the same.Please checkout the link below
Sample Starter Kit
For those who struggle with it, be sure the import class in your App.js are first
import configureStore from '../Redux/configureStore'
(where you configure your Navigation Middleware)
and second or after:
import ReduxNavigation from '../Navigation/ReduxNavigation'
(where you call createReduxBoundAddListener )
Otherwise you'll keep having this message

React native and redux persist not working

I am using redux-persist to store the data in my react-native app.
This is the code:
store.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import {
persistStore,
persistCombineReducers,
} from 'redux-persist';
import { AsyncStorage } from 'react-native';
import { composeWithDevTools } from 'redux-devtools-extension';
import user from './reducers/user';
import auth from './reducers/auth';
const config = {
key: 'root',
storage: AsyncStorage,
};
const reducers = persistCombineReducers(config, {
user,
auth
});
export const configureStore = () => {
const store = createStore(
reducers,
compose(
applyMiddleware(thunk),
)
);
const persistor = persistStore(store);
return { persistor, store };
};
Then in the App.js I have this :
const { persistor, store } = configureStore();
const onBeforeLift = () => {
// take some action before the gate lifts
store.dispatch(startingApp());
}
return (
<Provider store={store}>
<PersistGate
loading={<HomeLoader />}
onBeforeLift={onBeforeLift}
persistor={persistor}>
<RootNav />
</PersistGate>
</Provider>
Everything works fine when I dispatch and action from the App.js componentDidMount.
The problem is that when I fire the action from component, for example, the state is not stored, so when I restart the app the state is gone.
What I do in is just calling the action and passing the data:
this.props.onSetAuthData(data.credentials);
The state is updated as I can see in the console, but if I restart the app, only the state created by the action in App.js is saved, not the one in
Maybe this has to do with the RootNav component ?
maybe I am exporting wrong the reducers?
I have
const user = (state = initialState, action = {}) => {}
export default user.
Same for the other reducer:
const auth = (state = initialState, action = {}) => {}
export default auth.
Then I export with
combineReducers({auth, user})
Is this wrong?
Use tool like Reacttotron to see if your store is persisted or not.
https://github.com/infinitered/reactotron
If it's already persisted your component should wait until the store rehydrated on app launch. Sometimes I can't use the redux persist using persistgate to wait for the persisted store to be rehydrated. So I set the store and persistor into state on async componentWillMount then in your render, check if the store is not empty (null) and already rehydrated then load your app.
constructor(){
super();
this.state = {store: null, persistor: null}
}
async componentWillMount () {
const store = configureStore();
this.setState({ store: store.store })
this.setState({ persistor: store.persistor })
}
render(){
return (
if (this.state.store === null) {
return (
<View>
<Text>Loading...</Text>
</View>
);
}
<Provider store={this.state.store} persistor={this.state.persistor}>
<RootNav />
</Provider>
Also try to change your storage from AsyncStorage to storage.
const config = {
key: 'root',
storage,
};
First import the storage import storage from 'redux-persist/es/storage';
sometimes it calls an error with the key in persistConfig. try key: 'primary'
const primary = {
key: 'root',
storage: AsyncStorage,
blacklist: [],
whitelist: ['user'],
};

Component's prop doesn't update in React Native with Redux

I need some help with my app and Redux! (Currently, i hate it aha)
So, i have a notification page component which fetch some datas and i need to put the data length into my redux store to put badge on my icon in my tabbar!
My Main Reducer :
import { combineReducers } from "redux";
import NotificationReducer from "./NotificationReducer";
export default function getRootReducer(navReducer) {
return combineReducers({
nav: navReducer,
notificationReducer: NotificationReducer
});
}
My Notification reducer
const initialState = {
NotificationCount: 0
};
export default function notifications(state = initialState, action = {}) {
switch (action.type) {
case 'SET_COUNT' :
console.log('REDUCER NOTIFICATION SET_COUNT',state)
return {
...state,
NotificationCount: action.payload
};
default:
return state;
}
};
My Action :
export function setNotificationCount(count) {
return function (dispatch, getState) {
console.log('Action - setNotificationCount: '+count)
dispatch( {
type: 'SET_COUNT',
payload: count,
});
};
};
My Component :
import React, { Component } from 'react';
import { View, Text, StyleSheet, ScrollView, Dimensions, TouchableOpacity, SectionList, Alert } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { Notification } from '#Components';
import { ORANGE } from '#Theme/colors';
import { NotificationService } from '#Services';
import Style from './style';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '#Redux/Actions';
const width = Dimensions.get('window').width
const height = Dimensions.get('window').height
export class NotificationsClass extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
NotificationCount: undefined
};
}
async componentWillMount() {
this.updateNotifications();
}
componentWillReceiveProps(nextProps){
console.log('receive new props',nextProps);
}
async updateNotifications() {
this.props.setNotificationCount(10); <---
let data = await NotificationService.get();
if (data && data.data.length > 0) {
this.setState({ dataSource: data });
console.log(this.props) <-- NotificationCount is undefined
}
}
render() {
if (this.state.dataSource.length > 0) {
return (
<SectionList
stickySectionHeadersEnabled
refreshing
keyExtractor={(item, index) => item.notificationId}
style={Style.container}
sections={this.state.dataSource}
renderItem={({ item }) => this.renderRow(item)}
renderSectionHeader={({ section }) => this.renderSection(section)}
/>
);
} else {
return this.renderEmpty();
}
}
renderRow(data) {
return (
<TouchableOpacity activeOpacity={0.8} key={data.notificationId}>
<Notification data={data} />
</TouchableOpacity>
);
}
}
const Notifications = connect(
state => ({
NotificationCount: state.NotificationCount
}),
dispatch => bindActionCreators(Actions, dispatch)
)(NotificationsClass);
export { Notifications };
(I've removed some useless code)
Top Level :
const navReducer = (state, action) => {
const newState = AppNavigator.router.getStateForAction(action, state);
return newState || state;
};
#connect(state => ({
nav: state.nav
}))
class AppWithNavigationState extends Component {
render() {
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
})}
/>
);
}
}
const store = getStore(navReducer);
export default function NCAP() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
React : 15.6.1
React-Native : 0.46.4
Redux : 3.7.2
React-Redux : 5.0.5
React-Navigation : 1.0.0-beta.11
Node : 6.9.1
So if you've an idea! It will be great :D !
Thanks !
There's three issues.
First, React's re-rendering is almost always asynchronous. In updateNotifications(), you are calling this.props.setNotificationCount(10), but attempting to view/use the props later in that function. Even with the await in there, there's no guarantee that this.props.NotificationCount will have been updated yet.
Second, based on your reducer structure and mapState function, props.NotificationCount will actually never exist. In your getRootReducer() function, you have:
return combineReducers({
nav: navReducer,
notificationReducer: NotificationReducer
});
That means your root state will be state.nav and state.notificationReducer. But, in your mapState function, you have:
state => ({
NotificationCount: state.NotificationCount
}),
state.NotificationCount will never exist, because you didn't use that key name when you called combineReducers.
Third, your notificationReducer actually has a nested value. It's returning {NotificationCount : 0}.
So, the value you actually want is really at state.notificationReducer.NotificationCount. That means your mapState function should actually be:
state => ({
NotificationCount: state.notificationReducer.NotificationCount
}),
If your notificationReducer isn't actually going to store any other values, I'd suggest simplifying it so that it's just storing the number, not the number inside of an object. I'd also suggest removing the word Reducer from your state slice name. That way, you could reference state.notification instead.
For more info, see the Structuring Reducers - Using combineReducers section of the Redux docs, which goes into more detail on how using combineReducers defines your state shape.

How to listen to route changes in react router v4?

I have a couple of buttons that acts as routes. Everytime the route is changed, I want to make sure the button that is active changes.
Is there a way to listen to route changes in react router v4?
I use withRouter to get the location prop. When the component is updated because of a new route, I check if the value changed:
#withRouter
class App extends React.Component {
static propTypes = {
location: React.PropTypes.object.isRequired
}
// ...
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
this.onRouteChanged();
}
}
onRouteChanged() {
console.log("ROUTE CHANGED");
}
// ...
render(){
return <Switch>
<Route path="/" exact component={HomePage} />
<Route path="/checkout" component={CheckoutPage} />
<Route path="/success" component={SuccessPage} />
// ...
<Route component={NotFound} />
</Switch>
}
}
To expand on the above, you will need to get at the history object. If you are using BrowserRouter, you can import withRouter and wrap your component with a higher-order component (HoC) in order to have access via props to the history object's properties and functions.
import { withRouter } from 'react-router-dom';
const myComponent = ({ history }) => {
history.listen((location, action) => {
// location is an object like window.location
console.log(action, location.pathname, location.state)
});
return <div>...</div>;
};
export default withRouter(myComponent);
The only thing to be aware of is that withRouter and most other ways to access the history seem to pollute the props as they de-structure the object into it.
As others have said, this has been superseded by the hooks exposed by react router and it has a memory leak. If you are registering listeners in a functional component you should be doing so via useEffect and unregistering them in the return of that function.
v5.1 introduces the useful hook useLocation
https://reacttraining.com/blog/react-router-v5-1/#uselocation
import { Switch, useLocation } from 'react-router-dom'
function usePageViews() {
let location = useLocation()
useEffect(
() => {
ga.send(['pageview', location.pathname])
},
[location]
)
}
function App() {
usePageViews()
return <Switch>{/* your routes here */}</Switch>
}
You should to use history v4 lib.
Example from there
history.listen((location, action) => {
console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
console.log(`The last navigation action was ${action}`)
})
withRouter, history.listen, and useEffect (React Hooks) works quite nicely together:
import React, { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
const Component = ({ history }) => {
useEffect(() => history.listen(() => {
// do something on route change
// for my example, close a drawer
}), [])
//...
}
export default withRouter(Component)
The listener callback will fire any time a route is changed, and the return for history.listen is a shutdown handler that plays nicely with useEffect.
import React, { useEffect } from 'react';
import { useLocation } from 'react-router';
function MyApp() {
const location = useLocation();
useEffect(() => {
console.log('route has been changed');
...your code
},[location.pathname]);
}
with hooks
With hooks:
import { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { history as historyShape } from 'react-router-prop-types'
const DebugHistory = ({ history }) => {
useEffect(() => {
console.log('> Router', history.action, history.location)
}, [history.location.key])
return null
}
DebugHistory.propTypes = { history: historyShape }
export default withRouter(DebugHistory)
Import and render as <DebugHistory> component
import { useHistory } from 'react-router-dom';
const Scroll = () => {
const history = useHistory();
useEffect(() => {
window.scrollTo(0, 0);
}, [history.location.pathname]);
return null;
}
With react Hooks, I am using useEffect
import React from 'react'
const history = useHistory()
const queryString = require('query-string')
const parsed = queryString.parse(location.search)
const [search, setSearch] = useState(parsed.search ? parsed.search : '')
useEffect(() => {
const parsedSearch = parsed.search ? parsed.search : ''
if (parsedSearch !== search) {
// do some action! The route Changed!
}
}, [location.search])
in this example, Im scrolling up when the route change:
import React from 'react'
import { useLocation } from 'react-router-dom'
const ScrollToTop = () => {
const location = useLocation()
React.useEffect(() => {
window.scrollTo(0, 0)
}, [location.key])
return null
}
export default ScrollToTop
In some cases you might use render attribute instead of component, in this way:
class App extends React.Component {
constructor (props) {
super(props);
}
onRouteChange (pageId) {
console.log(pageId);
}
render () {
return <Switch>
<Route path="/" exact render={(props) => {
this.onRouteChange('home');
return <HomePage {...props} />;
}} />
<Route path="/checkout" exact render={(props) => {
this.onRouteChange('checkout');
return <CheckoutPage {...props} />;
}} />
</Switch>
}
}
Notice that if you change state in onRouteChange method, this could cause 'Maximum update depth exceeded' error.
For functional components try useEffect with props.location.
import React, {useEffect} from 'react';
const SampleComponent = (props) => {
useEffect(() => {
console.log(props.location);
}, [props.location]);
}
export default SampleComponent;
For React Router v6 & React Hooks,
You need to use useLocation instead of useHistory as it is deprecated
import { useLocation } from 'react-router-dom'
import { useEffect } from 'react'
export default function Component() {
const history = useLocation();
useEffect(() => {
console.log('> Router', history.pathname)
}, [history.pathname]);
}
With the useEffect hook it's possible to detect route changes without adding a listener.
import React, { useEffect } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import Main from './Main';
import Blog from './Blog';
const App = ({history}) => {
useEffect( () => {
// When route changes, history.location.pathname changes as well
// And the code will execute after this line
}, [history.location.pathname]);
return (<Switch>
<Route exact path = '/' component = {Main}/>
<Route exact path = '/blog' component = {Blog}/>
</Switch>);
}
export default withRouter(App);
I just dealt with this problem, so I'll add my solution as a supplement on other answers given.
The problem here is that useEffect doesn't really work as you would want it to, since the call only gets triggered after the first render so there is an unwanted delay.
If you use some state manager like redux, chances are that you will get a flicker on the screen because of lingering state in the store.
What you really want is to use useLayoutEffect since this gets triggered immediately.
So I wrote a small utility function that I put in the same directory as my router:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
Which I call from within the component HOC like this:
callApis(() => getTopicById({topicId}), path);
path is the prop that gets passed in the match object when using withRouter.
I'm not really in favour of listening / unlistening manually on history.
That's just imo.