Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NavigatorIOS: Accessing onRightButtonPress from within child component #31

Closed
nick opened this issue Feb 2, 2015 · 20 comments
Closed
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@nick
Copy link

nick commented Feb 2, 2015

I'm creating a TodoList example app using Flux. Right now I have three components: TodoListApp, TodoItems and AddItem. TodoListApp is simply a NavigatorIOS component which shows TodoItems by default and a method to push the AddItem page to the navigator stack when the user taps the '+' button.

My question is: is there a way for my AddItem component (child of NavigatorIOS) to handle the onRightButtonPress event? Right now I've had to put in a bunch of hacks to accommodate adding a todo item when the user taps the 'Save' button from the AddItem page.

@nick
Copy link
Author

nick commented Feb 2, 2015

btw I'd love to contribute this example to the official repo once it's done :-)

@vjeux
Copy link
Contributor

vjeux commented Feb 2, 2015

You are right, we need to have a todo app example otherwise we're not a real js lib :p

@nick
Copy link
Author

nick commented Feb 2, 2015

My thoughts exactly. I'm trying to give it the exact same functionality as the iOS demo app you build in the first tutorial. Except the tutorial for this will be about 1/10th the length ;-)

@zertosh
Copy link
Member

zertosh commented Feb 2, 2015

@nick I've got a similar issue - I need to be notified that a "back" happened #26. I'm currently hacking around it by having the NavigatorIOS children call a prop callback on componentWillUnmount.

@ericvicenti
Copy link
Contributor

Currently the best way to do that is to create an EventEmitter in the owner of the NavigatorIOS, then you can pass it down to children using route.passProps. The child can mix in Subscribable.Mixin and then in componentDidMount, you can this.addListenerOn(this.props.events, 'myRightBtnEvent', this._handleRightBtnPress);

It is clear that this API needs improvement. We are actively working the routing API in Relay, and hopefully react-router, but we want NavigatorIOS to be usable independently. Maybe we should add an event emitter inside the navigator object, so child components can subscribe to various navigator activity: this.addListenerOn(this.props.navigator.events, 'rightButtonPress', this._handleRightBtnPress);

Feedback welcome!

@nick
Copy link
Author

nick commented Feb 3, 2015

That last api looks good to me. In the meanwhile I'll try your suggestion. Can't wait to see what @ryanflorence and @mjackson do with support in react-router! Already using it in all my other projects.

@ReadingSteiner
Copy link

@ericvicenti
Why not use

this.props.event.addListener('myRightBtnEvent',this._handleRightBtnPress)` ?

directly in the componentDidMount?

What is Subscribable.Mixin's job here?

@ericvicenti
Copy link
Contributor

Subscribable.Mixin is something we use internally to make sure nobody forgets to unsubscribe to an emitter from a component on unmount. The mixin will automatically remove subscriptions added with this.addListenerOn. We are currently phasing it out, though, because we will be transitioning to observables in an upcoming version of React.

For more info on the existing EventEmitter, see the github project here: https://github.com/facebook/emitter

@ReadingSteiner
Copy link

@ericvicenti
Thanks a lot for ur reply :)

@backslash112
Copy link

@nick
Checkout this demo: react-native_flux_demo

@tbolt
Copy link

tbolt commented Mar 26, 2016

Flux being the only solution for this isn't ideal.

@backslash112
Copy link

@teebolt
I agree with you, because I met a new problem when I trying to use Flux:
I created a TodoItems app too, When I succeeded in pushing a notification to the child component, and the child component will save the new item to the db, after this, the child component will push another notification to the list component for reload the list, the problem is, there are two dispatches in this process and it will cause a error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.

@tbolt
Copy link

tbolt commented Mar 28, 2016

I was able to solve this using a simple event emitter. I can post more details if anyone needs.

@backslash112
Copy link

@teebolt Please post more details about simple event emitter, thank you.

@tbolt
Copy link

tbolt commented Mar 29, 2016

@backslash112 Here is how I was able to have the "onRightButtonPress" trigger a function in the sub-component.

The only dependency I used is https://github.com/Olical/EventEmitter which can be added:
var EventEmitter = require('wolfy87-eventemitter');

In my main file (index.io.js) I created a new event emitter outside of the class:
var rightButtonHandler = new EventEmitter();

Then, inside the class I created a method to be called when the button is pressed:
handleSaveButton() { rightButtonHandler.emitEvent('saveButtonPressed'); }

And right below that I am passing the event emitter as a property to the sub-component:
passProps: { events: rightButtonHandler }

Now, switching to the sub-component class, add this or update your componentDidMount method:
componentDidMount() { this.props.events.addListener('saveButtonPressed', this.saveConcert.bind(this)); }

The above will be calling this saveConcert:
saveConcert() { /// This is now called when the navigatorIOS right button is pressed. }

Hope this helps!

@backslash112
Copy link

@teebolt
Thank you very much, it works.
But if you want to start another dispatch in a listener that is triggered by emitChange() of a Store, you will get the same error like I said above: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.

@chetankothari
Copy link

chetankothari commented May 23, 2016

@ericvicenti I tried the approach you mentioned in one of the replies, thanks for the detailed explanation. Just wanted to check if the same holds true now or is there another way of solving it in a better way. If so could you please help me out with that.

Thanks.

@ericvicenti
Copy link
Contributor

These days I would just go with a redux-style control flow, but its not too easy to pass props down into the inside of NavigatorIOS scenes. But it can probably be done with a redux provider, and that would be a much cleaner solution

@chetankothari
Copy link

@ericvicenti Thanks for the prompt response. Will try out getting redux-style control flow.

dustturtle added a commit to dustturtle/react-native that referenced this issue Jul 6, 2016
…crash on simulator, on device I got nothing but app freezed)!

My app has an old version of JSONKit which is still using MRC. I think JSONKit is not needed if system version is available. Kicking out of JSONKit will make react native stronger.
Crash stack:
* thread facebook#11: tid = 0xbd672f, 0x000000010a10edeb imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=22 key/value pairs) + 16971 at JSONKit.m:2807, name = 'com.facebook.React.JavaScript', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000010a10edeb imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=22 key/value pairs) + 16971 at JSONKit.m:2807
    frame facebook#1: 0x000000010a10ef67 imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=2 key/value pairs) + 17351 at JSONKit.m:2811
    frame facebook#2: 0x000000010a10ef67 imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=25 key/value pairs) + 17351 at JSONKit.m:2811
    frame facebook#3: 0x000000010a10e768 imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=@"3 elements") + 15304 at JSONKit.m:2778
  * frame facebook#4: 0x000000010a10a26a imobii-waiqin`-[JKSerializer serializeObject:options:encodeOption:block:delegate:selector:error:](self=0x00007f9b831fbc80, _cmd="serializeObject:options:encodeOption:block:delegate:selector:error:", object=@"3 elements", optionFlags=0, encodeOption=10, block=0x0000000000000000, delegate=0x0000000000000000, selector=<no value available>, error=domain: class name = NSInvocation - code: 0) + 2250 at JSONKit.m:2876
    frame facebook#5: 0x000000010a109992 imobii-waiqin`+[JKSerializer serializeObject:options:encodeOption:block:delegate:selector:error:](self=JKSerializer, _cmd="serializeObject:options:encodeOption:block:delegate:selector:error:", object=@"3 elements", optionFlags=0, encodeOption=10, block=0x0000000000000000, delegate=0x0000000000000000, selector=<no value available>, error=domain: class name = NSInvocation - code: 0) + 178 at JSONKit.m:2831
    frame facebook#6: 0x000000010a10f700 imobii-waiqin`-[NSArray(self=@"3 elements", _cmd="JSONStringWithOptions:error:", serializeOptions=0, error=domain: class name = NSInvocation - code: 0) JSONStringWithOptions:error:] + 112 at JSONKit.m:2985
    frame facebook#7: 0x000000010ac13c02 imobii-waiqin`_RCTJSONStringifyNoRetry(jsonObject=@"3 elements", error=domain: class name = NSInvocation - code: 0) + 338 at RCTUtils.m:49
    frame facebook#8: 0x000000010ac13990 imobii-waiqin`RCTJSONStringify(jsonObject=@"3 elements", error=0x0000000000000000) + 128 at RCTUtils.m:77
    frame facebook#9: 0x000000010ab5fdfa imobii-waiqin`__27-[RCTContextExecutor setUp]_block_invoke_2(.block_descriptor=<unavailable>, moduleName=@"UIManager") + 218 at RCTContextExecutor.m:363
    frame facebook#10: 0x00000001134495cc CoreFoundation`__invoking___ + 140
    frame facebook#11: 0x000000011344941e CoreFoundation`-[NSInvocation invoke] + 286
    frame facebook#12: 0x000000010db13db3 JavaScriptCore`JSC::ObjCCallbackFunctionImpl::call(JSContext*, OpaqueJSValue*, unsigned long, OpaqueJSValue const* const*, OpaqueJSValue const**) + 451
    frame facebook#13: 0x000000010db13926 JavaScriptCore`JSC::objCCallbackFunctionCallAsFunction(OpaqueJSContext const*, OpaqueJSValue*, OpaqueJSValue*, unsigned long, OpaqueJSValue const* const*, OpaqueJSValue const**) + 262
    frame facebook#14: 0x000000010db14bad JavaScriptCore`long long JSC::APICallbackFunction::call<JSC::ObjCCallbackFunction>(JSC::ExecState*) + 573
    frame facebook#15: 0x000000010dade340 JavaScriptCore`JSC::LLInt::setUpCall(JSC::ExecState*, JSC::Instruction*, JSC::CodeSpecializationKind, JSC::JSValue, JSC::LLIntCallLinkInfo*) + 528
    frame facebook#16: 0x000000010dae535d JavaScriptCore`llint_entry + 22900
    frame facebook#17: 0x000000010dadf7d9 JavaScriptCore`vmEntryToJavaScript + 326
    frame facebook#18: 0x000000010d9b1959 JavaScriptCore`JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) + 169
    frame facebook#19: 0x000000010d9985ad JavaScriptCore`JSC::Interpreter::executeCall(JSC::ExecState*, JSC::JSObject*, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) + 493
    frame facebook#20: 0x000000010d76cb7e JavaScriptCore`JSC::call(JSC::ExecState*, JSC::JSValue, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) + 62
    frame facebook#21: 0x000000010d929a55 JavaScriptCore`JSC::callGetter(JSC::ExecState*, JSC::JSValue, JSC::JSValue) + 149
    frame facebook#22: 0x000000010dad49fb JavaScriptCore`llint_slow_path_get_by_id + 2203
    frame facebook#23: 0x000000010dae22f0 JavaScriptCore`llint_entry + 10503
    frame facebook#24: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#25: 0x000000010dae52fd JavaScriptCore`llint_entry + 22804
    frame facebook#26: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#27: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#28: 0x000000010dae52fd JavaScriptCore`llint_entry + 22804
    frame facebook#29: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#30: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#31: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#32: 0x000000010dae552a JavaScriptCore`llint_entry + 23361
    frame facebook#33: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#34: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#35: 0x000000010dadf7d9 JavaScriptCore`vmEntryToJavaScript + 326
    frame facebook#36: 0x000000010d9b1959 JavaScriptCore`JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) + 169
    frame facebook#37: 0x000000010d998264 JavaScriptCore`JSC::Interpreter::execute(JSC::ProgramExecutable*, JSC::ExecState*, JSC::JSObject*) + 10404
    frame facebook#38: 0x000000010d7a8786 JavaScriptCore`JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&) + 470
    frame facebook#39: 0x000000010d9f6fb8 JavaScriptCore`JSEvaluateScript + 424
    frame facebook#40: 0x000000010ab6379e imobii-waiqin`__68-[RCTContextExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke.264(.block_descriptor=<unavailable>) + 414 at RCTContextExecutor.m:589
    frame facebook#41: 0x000000010ab63262 imobii-waiqin`__68-[RCTContextExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke(.block_descriptor=<unavailable>) + 498 at RCTContextExecutor.m:589
    frame facebook#42: 0x000000010ab63df8 imobii-waiqin`-[RCTContextExecutor executeBlockOnJavaScriptQueue:](self=0x00007f9b832f6040, _cmd="executeBlockOnJavaScriptQueue:", block=0x00007f9b80c92970) + 248 at RCTContextExecutor.m:627
    frame facebook#43: 0x000000010eb1d7a7 Foundation`__NSThreadPerformPerform + 283
    frame facebook#44: 0x0000000113486301 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame facebook#45: 0x000000011347c22c CoreFoundation`__CFRunLoopDoSources0 + 556
    frame facebook#46: 0x000000011347b6e3 CoreFoundation`__CFRunLoopRun + 867
    frame facebook#47: 0x000000011347b0f8 CoreFoundation`CFRunLoopRunSpecific + 488
    frame facebook#48: 0x000000010ab5e41b imobii-waiqin`+[RCTContextExecutor runRunLoopThread](self=RCTContextExecutor, _cmd="runRunLoopThread") + 363 at RCTContextExecutor.m:284
    frame facebook#49: 0x000000010ebc012b Foundation`__NSThread__start__ + 1198
    frame facebook#50: 0x00000001140869b1 libsystem_pthread.dylib`_pthread_body + 131
    frame facebook#51: 0x000000011408692e libsystem_pthread.dylib`_pthread_start + 168
    frame facebook#52: 0x0000000114084385 libsystem_pthread.dylib`thread_start + 13
@littlehome-eugene
Copy link

@ericvicenti @chetankothari
Can you elaborate what you mean by redux-style control flow?

Notify the click event through redux state?

@facebook facebook locked as resolved and limited conversation to collaborators May 31, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests