I'm trying to call a component function on a child component RouteHandler.
var Bar = React.createClass({
baz: function() {
console.log('something');
},
render: function() {
return <div />;
},
});
var Foo = React.createClass({
baz: function() {
this.refs['REF'].baz();
},
render: function() {
return <RouteHandler ref="REF" />;
},
);
Where RouteHandler is a Bar but this.refs['REF'].baz is undefined.
See https://facebook.github.io/react/tips/expose-component-functions.html for more information on component functions.
I don't believe react-router currently supports exposing component functions on RouteHandlers and the current hacky workaround is to do:
this.refs['REF'].refs['__routeHandler__'].baz();
See https://github.com/rackt/react-router/issues/1597
Related
I have a Container component, it gets a few children.
on componentDidUpdate i would like to change a style attribute.
seems like the Container should be able to handle this, instead of each child handling itself.
Problem, react throws an error when trying to get Dom node of child.
var childA = this.props.children[0];
React.findDOMNode(childA);
=> Uncaught Error: Invariant Violation: Element appears to be neither ReactComponent nor DOMNode (keys: )
EDIT: That ^^^ was the wrong way to write react!
If your children need to do something, you should always try to pass it down from the top.
lets say you have 3 colors: green,red,blue and you want your children to be colored that way, but maybe they change order often. pass it down
Parent = React.createClass({
renderChildren: function(){
var colors = ["red", "blue", "green"]
return React.Children.map(this.props,children, function(child, index){
// this returns a legit clone, adding one extra prop.
return React.cloneElement(child, {color: colors[index]});
})
},
render: function(){
return (
<div>{this.renderChildren()}</div>
)
}
})
Child = React.createClass({
render: function(){
return (<div style={{background: this.props.color}}>{'YOLO'}</div>)
}
})
What you are trying to do is not a recommended practice when working with React. Don't modify the DOM yourself, use state and props.
Children components:
var ChildrenA = React.createClass({
render: function() {
return <div style={{color: this.props.color}}>Hello A</div>;
}
});
App:
var App = React.createClass({
render: function() {
return (<div>
<div>Hello {this.props.name}</div>
<div>
<Container/>
</div>
</div>);
}
});
Container:
var Container = React.createClass({
getInitialState: function(){
return {color: "red"}
},
toggle: function(){
this.setState({
color: this.state.color == "red" ? "blue" : "red"
})
},
render: function() {
return (<div onClick={this.toggle}>
<ChildrenA color={this.state.color}/>
<ChildrenB/>
</div>);
}
});
JSFiddle: https://jsfiddle.net/3m8wLcgk/
You might want to check this out: Thinking in React
Using React, I wish to get the audio element.
var AudioPlayer = React.createClass({
componentDidMount: function () {
console.info('Audio: component did mount');
var audio = React.findDOMNode('audio');
console.info('audio', audio);
},
render : function() {
return (
<audio src="/static/music/foo.mp3" controls />
);
}
});
But I keep receiving the error:
Error: Invariant Violation: Element appears to be neither ReactComponent nor DOMNode (keys: 0,1,2,3,4)
Surely lowered components are React classes?
It works using the component references:
var AudioPlayer = React.createClass({
componentDidMount: function () {
console.info('[AudioPlayer] componentDidMount...');
this.props.el = React.findDOMNode(this.refs.audio_tag);
console.info('audio prop set', this.props.el);
},
render: function() {
console.info('[AudioPlayer] render...');
return (
<audio ref="audio_tag" src="/static/music/foo.mp3" controls autoplay="true"/>
);
}
});
I am trying to call a child function from the right button on the parent navigator.
A basic code example of what I need is as follows:
Main.js
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Test',
component: Test,
rightButtonTitle: 'Change String',
onRightButtonPress: () => ***I Want to call miscFunction from here*** ,
passProps: {
FBId: this.state.fbidProp,
favsPage: true
}
}}/>
Test.js
class Test extends React.Component{
constructor(props){
super(props);
this.state = {
variable: 'some string'
};
}
miscFunction(){
this.setState({
variable: 'new string'
};
}
render(){
return(
<Text> {variable} </Text>
)
}
}
This is covered in the following github issue:
https://github.com/facebook/react-native/issues/31
Eric Vicenti comments to describe how Facebook solves this internally:
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);
Here's how this looks in a practical example:
'use strict';
var React = require('react-native');
var EventEmitter = require('EventEmitter');
var Subscribable = require('Subscribable');
var {
AppRegistry,
StyleSheet,
Text,
View,
NavigatorIOS
} = React;
First we pull in all of our requirements including the EventEmitter and Subscribable.
var App = React.createClass({
componentWillMount: function() {
this.eventEmitter = new EventEmitter();
},
onRightButtonPress: function() {
this.eventEmitter.emit('myRightBtnEvent', { someArg: 'argValue' });
},
render: function() {
return <NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Test',
component: Test,
rightButtonTitle: 'Change String',
onRightButtonPress: this.onRightButtonPress,
passProps: {
events: this.eventEmitter
}
}}/>
}
});
In our main top-level component, we create a new EventEmitter (in componentWillMount) to be available across the component, and then use passProps to pass it down to the Test component we specify for the navigator.
We also define a handler for the right button press, which emits a myRightBtnEvent with some dummy arguments when that button is pressed. Now, in the Test component:
var Test = React.createClass({
mixins: [Subscribable.Mixin],
getInitialState: function() {
return {
variable: 'original string'
};
},
componentDidMount: function() {
this.addListenerOn(this.props.events, 'myRightBtnEvent', this.miscFunction);
},
miscFunction: function(args){
this.setState({
variable: args.someArg
});
},
render: function(){
return(
<View style={styles.scene}><Text>{this.state.variable}</Text></View>
)
}
});
We add the Subscribable mixin, and the only other thing we need to do is listen out for the myRightBtnEvent being fired from the App component and hook miscFunction up to it. The miscFunction will be passed the dummy arguments from the App press handler so we can use those to set state or perform other actions.
You can see a working version of this on RNPlay:
https://rnplay.org/apps/H5mMNQ
A. In initial component
this.props.navigator.push({
title: 'title',
component: MyComponent,
rightButtonTitle: 'rightButton',
passProps: {
ref: (component) => {this.pushedComponent = component},
},
onRightButtonPress: () => {
// call func
this.pushedComponent && this.pushedComponent.myFunc();
},
});
B. In pushed component
replace onRightButtonPress func in pushed component.
componentDidMount: function() {
// get current route
var route = this.props.navigator.navigationContext.currentRoute;
// update onRightButtonPress func
route.onRightButtonPress = () => {
// call func in pushed component
this.myFunc();
};
// component will not rerender
this.props.navigator.replace(route);
},
You can use the flux, here is a demo: https://github.com/backslash112/react-native_flux_demo
I use this simple React component only for example.
I would like to access this.setState() inside the functions 'working' and 'group.notWorking'.
var myComponent = React.createClass({
getInitialState: function() {
return {};
},
working: function() {
this.setState({ test: true }); //this is myComponent
},
group: {
notWorking: function() {
console.log(this); //this is window
}
},
render: function() {
return (
<div>
<ChildComponent working={this.working} group={this.group}/>
</div>
);
},
});
My question is how do you pass functions grouped in an object, or is there any best practice, to avoid passing all the functions one by one to children components.
You need to pass a bound version of it.
<ChildComponent working={this.working} group={this.group.notWorking.bind(this)}/>
If you want to pass the whole group you need to make it a function which returns an object and bind it:
group: function() {
return {
notWorking: function() {
console.log(this);
}.bind(this)
};
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Consider this example which won't compile:
/** #jsx React.DOM */
var Hello = React.createClass({
render: function() {
return <div>Hello</div>;
}
});
var World = React.createClass({
render: function() {
return <div>World</div>;
}
});
var Main = React.createClass({
render: function() {
return (
<Hello />
<World />
);
}
});
React.renderComponent(<Main />, document.body);
But any of these combinations does work:
<div>
<Hello />
<World />
</div>
<Hello />
//<World />
//<Hello />
<World />
Wanted to know if multiple components should always be surrounded by div tags.
I think the render function should return only one component.
From the docs: http://facebook.github.io/react/docs/component-specs.html
The render() method is required.
When called, it should examine this.props and this.state and return a single child component
There's a simple way to get around this limitation:
var Hello = React.createClass({
render: function() {
return <div>Hello</div>;
}
});
var World = React.createClass({
render: function() {
return <div>World</div>;
}
});
var HelloWorld = [Hello, World];
var Main = React.createClass({
render: function() {
return (
{HelloWorld}
);
}
});
React.renderComponent(<Main />, document.body);
React components can only render a single root node. If you want to
return multiple nodes they must be wrapped in a single root.
As specified on the Official React Site: React Docs