React-native-router-flux: Actions.callback is not defined!

Created on 16 May 2016  Â·  31Comments  Â·  Source: aksonov/react-native-router-flux

Version

  • react-native-router-flux v3.22.8
  • react-native v0.24.0-rc5

    Issue

I'd expect that as soon as the Router is rendered, Actions has a callback for all scenes to transition to.

In my app I tried to transition to a login scene if the user isn't logged in at start. However, I get an error as soon as Actions.login() in the componentDidMount() function is executed:

Actions.callback is not defined!

If I try to do the same from within the Main.componentDidMount() function it works as expected.

At which point are the Scene callbacks added to Actions? Am I doing it wrong?

const reducerCreate = params => {
  const defaultReducer = Reducer(params);
  return (state, action) => {
    console.log('ACTION:', action);
    return defaultReducer(state, action);
  };
};
componentDidMount() {
  if (!loggedIn()) {
    Actions.login();
  }
},
render() {
  return (
    <Router createReducer={reducerCreate}>
      <Scene key="root" hideNavBar>
        <Scene key="login" component={Login} />
        <Scene key="main" component={Main} initial />
      </Scene>
    </Router>
  );
}
question

Most helpful comment

Is this fixed? I don't understand what I should do to get over this? Where is the right spot to call Actions.methods.. ? If not in componentDidMount? where?

Thanks Much

All 31 comments

@dominicmh
FYI https://github.com/aksonov/react-native-router-flux/issues/525

Considering https://github.com/aksonov/react-native-router-flux/issues/636 and that RN26 will be released within the next couple of days please close this issue

I'll close this issue as @rturk said 0.26.0 version will be landed soon. (react-native and react-native-router-flux)

If you still get this issue with 0.26.0, please reopen it.

I updated to RN 0.26.0, but the issue remains:

[react-native-router-flux] Actions.callback is not defined!

I used the 0.26-wip branch as suggested here #636 in order to make RNRF runnable on RN 0.26.0

@dominicmh paste more details of your code in a Gist.

@dominicmh My Two cents
looks like your app is rendering before Actions is loaded. In my case my app needs to load a lot of stuff from asyncStorage before boot so I was facing similar problems.

This can help:
https://github.com/fbsamples/f8app/blob/master/js/setup.js#L60

You may define all your scenes separately, before router to avoid that (however hot-loaded will not work). Or re-design your app properly, like @rturk suggests.

I'm running into this issue as well as I'm trying to redirect to an internal page from a notification to the phone. I'm a bit confused as to why the Actions methods for an interal page wouldn't be there as shown in @dominicmh 's App.js example. The way I understand it, per the documentation of React's componentDidMount:

Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. At this point in the lifecycle, you can access any refs to your children (e.g., to access the underlying DOM representation). The componentDidMount() method of child components is invoked before that of parent components.

Being that the children are rendered first and their componentDidMounts have fired, doesn't it only make sense that they would've registered with Actions?

All that being said, that's just my thoughts on how it should work. I wasn't able to figure out from this thread what you guys are proposing doing to make it work. If you could provide a clear example of this that would be fantastic!

"react-native": "0.28.0",
"react-native-router-flux": "3.31.0",

According to @rturk, if I'm understanding his proposed solution, he's attaching a callback to the store being configured which is getting called here: https://github.com/fbsamples/f8app/blob/master/js/store/configureStore.js#L50

This is when the store is configured by fetching the data from the local storage - this has nothing to do with the scenes being rendered - is this not just a race condition that seems to be working?

@aksonov your proposed solution of moving the defining the scenes separately and before the router is just another race condition, no? I guess I'm lost as to what is asynchronous about the scene callbacks being registered.

@aksonov @dwilt were you able to overcome this issue? I had to put a random setTimeout of some arbitrary amount to make this work. What would be beneficial is to provide a callback hook for when the actions are loaded since they seem to be loaded async?

@aksonov do you think that might work?

@shilpan the callback is an excellent idea. Or a promise would work too. And yes, I'm using an arbitrary timeout as well (1 second). Obviously far from ideal.

So finally I understood what the problem is.
The problem is not the Scenes taking time to initialize.
Action.login needs to have a the Actions.callback that the Router injects after first actual render which is after the root componentDidMount.

Here's what happens:
Root render function called
Router render function called without state.reducer so does nothing.
Router componentDidMount called - Here the Actions get initialized and the reducer gets saved to the state (setState is async).
Root componentDidMount - here the callback is still not initialized.
Router render called - This call is triggered by the setState before. Now the Actions.callback is injected.

So after second render, the router is initialized.

I managed to find a better solution than an arbitrary setTimeout.
You override the Router render method, and right after it's rendered with data, you notify the parent:

class OverrideRouter extends Router {
    constructor(props) {
        super(props);
        this.render = this.overrideRender;
    }
    overrideRender() {
        const result = super.render();
        if (!result) {
            return result;
        }
        // after first initialization use the regular render.
        this.render = super.render;
        if (this.props.onRouterInitialize) {
            // need this setTimeout to allow this method to complete so that the Actions.callback is populated
            setTimeout(this.props.onRouterInitialize, 10);
        }
        return result;
    }
}

@atlanteh I'm really curious that you seem to think you have solved this but it seems to be a bit of a workaround in the mean time for a couple of reasons:

  1. you're extending the base Router class instead of this being a solution that is baked directly into the core Router code
  2. The fact that you have a timeout there, even of 10ms seems a bit of a race condition.

I think there has to be a spot, and I acknowledge you for trying to find, that is a true hook when everything is complete and the user can pass in something like your onRouterInit and can be sure it's ready to go and comes out of the box with the Router and isn't based on a potential race condition.

Thoughts?

@dwilt I didn't say I solved it, I said I found a better solution than the arbitrary timeout solution.
But, I looked into it more, and managed to totally solve it :)
I moved the handleProps function to the constructor and removed the async setState function

@atlanteh sorry man, wasn't trying to pick on you - just didn't want that type of solution to become the standard going forward. So happy you made a PR. Thanks!

We did it for earlier versions but it would not work for Redux/Provider (wrappers), right?

On 03 Sep 2016, at 20:56, atlanteh [email protected] wrote:

@dwilt https://github.com/dwilt I didn't say I solved it, I said I found a better solution than the arbitrary timeout solution.
But, I looked into it more, and managed to totally solve it :)
I moved the handleProps function to the constructor and removed the async setState function

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/aksonov/react-native-router-flux/issues/686#issuecomment-244563838, or mute the thread https://github.com/notifications/unsubscribe-auth/ABQpcUfJAkLj7Eipj1IyT5x3SjARy17Cks5qmcLLgaJpZM4IfUpY.

@dwilt I didn't think you were. I was just making my point clearer :)

@aksonov I'v applied the fix to my app which does have Redux and it seems to work great. What failed for you then?

Any updates?

@atlanteh you're waiting on a PR review for #1137 right from @aksonov ?

I'd love to get this in as I still have my hacky setTimeout patch

Yes. Please check it in your app and update so that they can check it in.
Thx!

@atlanteh you want me to check what into my app? The changes that you made? I figured I'd wait till it got integrated into react-native-router-flux once the PR got merged then just do an update.

@dwilt I wish we could, but @joenoon wants others to check this PR before he merge it in..This PR is rather sensitive as it touches the core of rnrf so it's better if someone else checks it before we rush into it.
What you can do is replace the rnrf dependency in package.json with this:

"react-native-router-flux": "git://github.com/atlanteh/react-native-router-flux.git#patch-1"

Then perform an "npm install" to install my PR, and check that nothing breaks.

@atlanteh ok, installed it but how do I actually use it. Did you expose a promise/callback that I can call to make sure that everything is ready?

When componentDidMount of the Router's parent is invoked, the Router is ready to go :)

How can I tie into that though? The issue is that we're calling Actions methods which are not available yet. How can I know in my component that they are available?

FYI merged #1137

@dwilt, The problem is not Actions.foo() which doesn't exist, the problem is that this method calls Actions.callback function which gets _asynchronously_ initialized..This is the problem. My PR makes sure it gets initizlized synchoronously so that in the parent componentDidMount, the Router and Actions are fully initialized.
But @joenoon already merge this fix :)

Got it.

Is this fixed? I don't understand what I should do to get over this? Where is the right spot to call Actions.methods.. ? If not in componentDidMount? where?

Thanks Much

Sorry to add to the noise, but is there a fix for this?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

booboothefool picture booboothefool  Â·  3Comments

sylvainbaronnet picture sylvainbaronnet  Â·  3Comments

maphongba008 picture maphongba008  Â·  3Comments

sarovin picture sarovin  Â·  3Comments

fgrs picture fgrs  Â·  3Comments