ionSnapper is undefined
keiranvv opened this issue · comments
Hi,
I'm trying to use the IonSideNav set of components, however, I'm getting the above error when I try to use it.
Here's an example of the code from my App component (let me know if I need to include anything else):
<IonBody location={this.props.location} >
<IonSideMenuContainer>
<IonSideMenus>
<IonSideMenu>...</IonSideMenu>
</IonSideMenus>
<IonSideMenuContent>
<IonNavBar customClasses="bar-light"
title="My App"
leftButton={this.navButton()} />
<IonNavView>
<IonView>
{this.props.children}
</IonView>
</IonNavView>
</IonSideMenuContent>
</IonSideMenuContainer>
</IonBody>
Are you using contextTypes in your App component.
You can see an example of a component using it.
https://github.com/reactionic/reactionic/blob/master/src/components/ionBody.js
What @gabrielseco says. Also, have a look at this page to see how to control the sidemenus with clicks next to dragging: https://github.com/reactionic/reactionic-kitchensink/blob/master/app/client/imports/components/layouts/main.jsx
@gabrielseco do you mean the following?
contextTypes: {
ionSnapper: React.PropTypes.object
},
If so, then yes I have added it.
@pors That is indeed the example I've followed :)
@keiranvv yes, i meant that.
Could you show us the entire component to see where the problem is
Sure thing:
App.jsx:
import React, { Component } from 'react'
import { IonBody, IonIcon, IonNavBar, IonNavView, IonSideMenuContainer, IonSideMenuContent, IonSideMenu, IonSideMenus, IonView } from 'reactionic'
import SideMenu from './sidemenu'
export default App = React.createClass({
contextTypes: {
ionSnapper: React.PropTypes.object
},
render() {
return (
<IonBody location={this.props.location}>
<IonSideMenuContainer>
<IonSideMenus>
{this.sideMenu()}
</IonSideMenus>
<IonSideMenuContent>
<IonNavBar customClasses="bar-light"
title="Oviation"
leftButton={this.navButton()} />
<IonNavView>
<IonView>
{this.props.children}
</IonView>
</IonNavView>
</IonSideMenuContent>
</IonSideMenuContainer>
</IonBody>
)
},
navButton() {
if (Meteor.userId() === null) return
return (
<a onClick={() => { this.context.ionSnapper.toggle('left') }} className="button button-icon icon ion-navicon"></a>
)
},
sideMenu() {
if (Meteor.userId() === null) return
return (
<IonSideMenus>
<SideMenu />
</IonSideMenus>
)
}
})
SideMenu.jsx
import React from 'react'
import { IonSideMenu } from 'reactionic'
export default SideMenu = React.createClass({
contextTypes: {
ionSnapper: React.PropTypes.object
},
render() {
return (
<IonSideMenu customClasses="side-menu">
<div className="bar bar-header bar-stable">
<h1 className="title">Left Menu</h1>
</div>
<div className="content has-header side-menu">
<div className="list">
</div>
</div>
</IonSideMenu>
)
}
})
Well, I kinda detected the problem.
The ionBody component sets the snapper to null.
You can see the ionSnapper state is null. line 44.
https://github.com/reactionic/reactionic/blob/master/src/components/ionBody.js
Well if in your App.jsx remove the ionBody, the ionSnapper works.
So in the https://github.com/reactionic/reactionic-kitchensink/blob/master/app/client/router.jsx.
Import your App.jsx without the IonBody component in the router.jsx.
I would change the name because there's an App component with the IonBody component.
let mainRoute = (
{pageRoutes}
);
Tell me if it is this what you needed
removing the ionBody doesn't seem to work. I'm in the process of copying the kitchen sink setup line by line, but it still doesn't seem to be working.
I'll carry on and update if i resolve it.
Check this repo that I made
https://github.com/gabrielseco/snapperExample
In the https://github.com/gabrielseco/snapperExample/blob/master/app/client/router.jsx, I import your component that I recalled Snapper
this.context.ionSnapper is still null.
Can you see anything wrong with my router.jsx?
Router.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import { IndexRoute, Router, Route, browserHistory } from 'react-router'
import App from '../imports/ui/app/app'
import Main from '../imports/ui/app/main'
import Forms from '../imports/ui/forms/forms'
import FormEdit from '../imports/ui/forms/form_edit'
const main = () => {
let mainRoute = (
<Route component={Main}>
<IndexRoute component={Forms} />
<Route path='/forms/new' component={FormEdit} />
</Route>
);
var routes = (
<Route path="/" component={App}>
{ mainRoute }
</Route>
);
ReactDOM.render(<Router history={browserHistory}>{routes}</Router>, document.getElementById('root')) ;
}
Meteor.startup(main)
App.jsx
import { IonBody } from 'reactionic'
import React from 'react'
import { getPlatform } from '../utils/helper'
export default App = React.createClass({
getInitialState() {
return {platformOverride: this.props.location.query.platformOverride};
},
componentWillReceiveProps: function(newProps) {
var newPlatformOverride = newProps.location.query.platformOverride;
if (newPlatformOverride) {
if (newPlatformOverride !== this.state.platformOverride) {
this.setState({platformOverride: newPlatformOverride});
}
}
},
render() {
var platform = getPlatform(this.state.platformOverride);
return (
<IonBody platform={platform} location={this.props.location} >
{ React.cloneElement(this.props.children) }
</IonBody>
)
}
})
If you can't see anything wrong then I'm probably going to have to write my own slide out menu, as I can't see any other clear reason that this isn't working.
The App.jsx is the same that I have.
In the router.jsx my Main Component load in mainRoute, probably is different.
Look at this component.
https://github.com/gabrielseco/snapperExample/blob/master/app/client/imports/components/snapper.jsx
Ok, well I copied that component as is, and it still does the same thing. I'm going to have to write my own slide menu component for this app.
Thanks so much for all your help, really appreciate it.
So, I potentially found the bug I think?
I originally added the package using meteor's command, ie. meteor npm i -S reactionic
I tried removing the package: meteor npm uninstall -S reactionic
and then re-adding with npm i -S reactionic
without the meteor
keyword, and it worked!
Glad you solved it.
Edit 3 -- Solution:
If you are new to React Context, there a few things to note. This package keeps track of the UI state in the highest level context, at the very top level of the app. Your app should be designed (forcefully) this way:
<YourAppWrapper /*wraps your app with ionic functionality*/>
<IonBody /*this tracks the UI state via React's context*/>
<YourApp /*anything in your app under here can access the UI state via the context*/>
//closing tags
Every component class you export which falls under the IonBody
can access the UI/cosmetic state of the app, and has the ability to manipulate that state only if you specify Component.contextTypes
, which works analogously to Component.propTypes
. [I think] manipulating the state will re-diff your entire app, because your entire app sits inside of IonBody
. Is this performant? That's another conversation.
Things to check:
- Specify the right
contextTypes
in each component you build, if you want to interface with ionic's UI/cosmetic capabilities. - Do not use
ionSnapper
directly. For some reason, theIonBody
state change is not triggering a child context change, leaving the context's ionSnapper asnull
. This must be a problem with context or the design ofIonBody
, orIonBody
's integration of context may be outdated. - Use 'ionGetSnapper' instead, which is guaranteed to reference the current state of the
IonBody
's snapper.
//component that needs ionSnapper.
toggleSnapper(){
this.context.ionGetSnapper().toggle('left');
}
render(){
return (
//jsx, uses this.toggleSnapper onClick somewhere.
)
}
@keiranvv that solution did not work for me, the app still makes it null
.
I am curious why this package uses react's context, when the context feature is highly experimental. Inevitable changes in this feature would certainly break this package. This package claims to be "production ready", which I think might be false advertising. That said, I do think this package is awesome.
Edit 1:
this.context.ionSetSnapper(snapper);
is being called in the sideMenuContainer file. For some reason, the ionBody is not setting the context properly. I am guessing this is because of the problem above -- do not use context, it will change and break your package! I will look more into it.
Edit 2:
this.setState({ionSnapper: snapper})
is being called exactly once, and snapper !== undefined
. For some reason, the change in state is not updating the context, OR the context updates are not propagating to children. Will keep posted.
Context is not highly experimental as far as I know. Even react-router is
using it.
On Saturday, May 28, 2016, Streemo notifications@github.com wrote:
@keiranvv https://github.com/keiranvv that solution did not work for
me, the app still makes it null.
I am curious why this package uses react's context, when the context
feature is highly experimental. This package claims to be "production
ready", which I think might be false advertising.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#41 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/ABB0MAEkTTf14LyeqFe9xd7iCHe2QlKKks5qF3OmgaJpZM4IbQSd
.
https://facebook.github.io/react/docs/context.html
This advises not to use context. Is that only for React 1? Is there a newer version of React in which context is stable?
Thanks.
Yes context is bad practice for applications but very handy for libraries
where you don't want to make assumptions for the developer.
On Saturday, May 28, 2016, Streemo notifications@github.com wrote:
https://facebook.github.io/react/docs/context.html
This advises not to use context. Is that only for React 1? Is there a
newer version of React in which context is stable?
Thanks.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#41 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/ABB0MLXnf-RdwBMZTZe8WxxvWyK7y1YCks5qF4KRgaJpZM4IbQSd
.
I'm still having issues getting null for snapper.