Get Rendered Layer Onclick React - javascript

var Component = React.createClass({
onClickButton : function(){
//i want layer in this function
},
render: function () {
return (
<div className="Component">
<button onClick={this.onClickButton}>Click Me</button>
</div>
);
}
});
function renderNow(data,layer){
ReactDOM.render(
<Component data={data} />,
layer
);
}
called using renderNow({name: 'John' },someLayer);
I want layer inside the onClickButton function which was passed through renderNow function.
I tried passing layer to render function and setState the layer but it gave me StackOverflow Error

I am not sure what exactly the layer is and what you would like to achieve, but I suppose you should be able to pass layer as a props to your Component.
function renderNow(data,layer){
ReactDOM.render(
<Component data={data} layer={layer} />,
layer
);
}
and then
...
onClickButton : function(){
this.props.layer //i want layer in this function
},
...

Marek's answer is the way I would do it...but there's another way using ReactDOM.findDOMNode()
var Component = React.createClass({
onClickButton : function() {
//i want layer in this function
const layer = ReactDOM.findDOMNode(this); // <-- Here you go
},
render: function () {
return (
<div className="Component">
<button onClick={this.onClickButton}>Click Me</button>
</div>
);
}
});
Ref: https://facebook.github.io/react/docs/react-dom.html
Notice:
The docs generally recommend against breaking through the Component abstraction:
Note:
findDOMNode is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction.

Related

Can't call function in parent component from child component using React

I have 2 files:
grid-body.jsx (GridBody) and grid-row.jsx (GridRow)
In GridBody, I declared a function showAlert which I pass to every GridRow:
var GridBody = React.createClass({
showAlert: function(msg) {
alert(msg);
},
render: function() {
var rows = this.props.rows.map(function(li) {
return (
<GridRow showAlert={this.showAlert} />
);
});
return (
<div>
{rows}
</div>
);
}
});
And in GridRow:
var GridRow = React.createClass({
toggle: function() {
this.props.showAlert('HEY'); // -----> ERROR - not a function
},
render: function() {
<div>
<a href="#" onClick={this.toggle} />
</div>
}
});
I'm trying to call the showAlert from parent and based on the examples I've seen, this is how to do it but I can't make it work.
you're using the wrong value for this inside of GridView.render. Either pass it explicitly to Array.map() (see the docs for how to do that) or assign this to some new variable at the very top of render() and reference that instead.
Here is a really, really great SO comment as to why this happens, as well as some other alternative workarounds if neither of the above work for you.
The context of the function passed to map in render method of GridBody is window and not the component. You can bind the interatee to get the behavior you want:
render: function() {
var rows = this.props.rows.map(function(li) {
return (
<GridRow showAlert={this.showAlert} />
);
}.bind(this));
return (
<div>
{rows}
</div>
);
}

Passing props to generic children

Is there way to pass props to a generic child (not a component that you know ahead)?
Something that would make Wrapper be able to pass foo to children.
var Wrapper = React.createClass({
render: function () {
return <div>
{this.props.children foo={2}}
</div>
}
});
var App = React.createClass({
render: function () {
return (
<Wrapper>
{this.props.foo}
</Wrapper>
)
}
});
jsfiddle
Imagine Javascript code
this.props.children foo=2
this is what your expression is transpiled into from JSX into plain JS. The fact is, you can't pass props to children directly because children isn't a React component. To make it work, you need to map through children and pass your props per every item of iterable.
The problem that comes next is that you can't simply do
this.props.children.map(child => (
<Child foo={2} />
))
because, first, you'll receive TypeError because map is undefined, and second, you'd lose all initial props of every child.
You'll need to use React.Children.map static function as well as React.cloneElement to make it work:
React.Children.map(children, child => React.cloneElement(child, {
foo: 2
}))
This way, every child element preserves its own props passed from parent element and, in addition to them, receive new props you define. Be careful with it because you may unintentionally redefine values of some props, too.
Your example code will then look like
var Wrapper = React.createClass({
render: function () {
const {
foo
} = this.props;
return (
<div>
{React.Children.map(this.props.children, child => React.cloneElement(child, {
foo
}))}
</div>
);
}
});
var App = React.createClass({
render: function () {
return (
<Wrapper foo={2}>
<div>I should be a component</div>
<div>I should be a component, too</div>
<div>We all should be components</div>
</Wrapper>
);
}
});

Using jQuery inside of a React render function

I have the following React render function:
render: function () {
return (
<Popup onClose={this.props.onClose}>
<Form entity="strategy" edit="/strategies/edit/" add="/strategies/add/">
<h2>Create/Edit Strategy</h2>
<StrategyForm pending={this.state.pending} formData={this.state.data} />
<div className="col-md-6">
<Assisting />
</div>
</Form>
</Popup>
);
}
I would like to make the h2 heading be based on the body class, so my question is...can I do this?
render: function () {
return (
<Popup onClose={this.props.onClose}>
<Form entity="strategy" edit="/strategies/edit/" add="/strategies/add/">
if ( $('body').hasClass("this") ) {
<h2>Create This Strategy</h2>
} else {
<h2>Create Another Strategy</h2>
}
<StrategyForm pending={this.state.pending} formData={this.state.data} />
<div className="col-md-6">
<Assisting />
</div>
</Form>
</Popup>
);
}
If this is a terrible idea, can someone tell me what is a better way to do this in React?
As has already been noted in some of the comments on the OP, you could do it, but it's not really the "React" way.
A better solution would probably be to pass a prop into the usage of your component or have a flag on the state of your component -- then use that prop/flag to render.
Pseudocode:
render() {
return (
if (this.props.someProp) {
<h2>Create this Strategy</h2>
} else {
<h2>Create this Strategy</h2>
}
);
}
IMO using jQuery in the component methods is fine (e.g. componentDidMount(), or other event/utility methods) but usually you'll want to avoid this in render(). The whole purpose of React components is maintaining state, so on-the-fly usage of jQuery like your example defeats that idea.
Let's say for example you're rendering your component this way:
ReactDOM.render(<MyComponent />, document.getElementById('some-div'));
You can pass properties to your component:
ReactDOM.render(
<MyComponent someProp={true} />,
document.getElementById('some-div')
);
Or in your case:
ReactDOM.render(
<MyComponent someProp={$('body').hasClass("this")} />,
document.getElementById('some-div')
);
...something like that. It's an over-simplified example (not tested, so beware syntax errors) but that should help explain my thought process.
Alternatively, you use the componentDidMount() method on your class.
componentDidMount() {
this.setState({
someProp : $('body').hasClass("this")
});
}
and then in render() check against this.state.someProp.

react passing function to child not working

I have a react component that passes a function to a child component, and I bind it to onClick. But when I click I get an error:
this.props.beBad is not a function, this.props.beBad is not defined
it looks something like this:
var Dad = React.createClass({
beBad: function(someInput) {
alert('being bad ' + someInput);
},
render: function() {
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad} key={index}/>
);
});
return (
<div>
{Children}
</div>
);
});
var Child = React.createClass({
beBad: function() {
this.props.beBad('some input');
},
render: function() {
return(
<div onClick={this.beBad}>
be bad
</div>
);
}
});
It doesn't look like this is what you expect. When using React.createClass, React will autobind the correct this IFF the call is at the top level of the function, so in this case it will autobind correctly:
render: function() {
return (
<div onClick={this.beBad}> // autobound
be bad
</div>
);
}
But not here:
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad} key={index}/> // `this` is not what you expect
);
});
return (
<div>
{Children}
</div>
);
The reason is because map is creating a new scope, and this is no longer what you'd expect, and React can't autobind it for you. You need to pass this around explicitly:
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad.bind(this)} key={index}/> // bind `this`
);
}, this); // pass `this`
Note: Seeing the other response, it is possible that React autobinds it for you inside the map, but in general I wouldn't rely on autobinding, explicit wins over implicit. Plus if you ever write React with ES6 classes it doesn't do autobinding.
You've got a problem with your this because you're on the "map scope".
Try to pass this to your map :
var Children = this.state.children.map(function(data, index) {
return (
<Child beBad={this.beBad} key={index}/>
);
}, this);
For your information see : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Passing Parameters to Components in React Native

I'm trying to use a common Navigation Component I made in React-Native. At the point of calling I want to set the Navigation Bar Tint, Title etc.
Nav Bar Code:
var NavigationBar = React.createClass({
render: function(title, titleColor, NavBarColor) {
var titleConfig = {
title: title,
tintColor: titleColor,
};
return (
<NavBar
title={titleConfig}
tintColor={NavBarColor}
leftButton={<Button style={styles.menuButton}></Button>}
rightButton={<Button style={styles.menuButton}></Button>} />
);
}
});
Applying it on another page:
<NavigationBar title="Categories" titleColor="#ffffff" NavBarColor="#f0b210"/>
How to do this properly? Thanks in advance.
First off render does not take any parameters, what you want to do is to reference your props that you passed in.
render: function () {
var titleConfig = {
title: this.props.title,
tintColor: this.props.titleColor
};
// Rest of code
}
Just by doing this, whenever your NavigationBar rerenders so will the NavBar component too.
A super simple example demonstrating this
var NavBar = React.createClass({
render: function () {
return <div id="navbar" style={{backgroundColor: this.props.tintColor}}>
<h1 style={{color: this.props.title.tintColor}}>{this.props.title.title}</h1>
</div>;
}
});
var NavigationBar = React.createClass({
render: function() {
var titleConfig = {
title: this.props.title,
tintColor: this.props.titleColor,
};
return (
<NavBar
title={titleConfig}
tintColor={this.props.NavBarColor}
/>
);
}
});
React.render(<NavigationBar title="Categories" titleColor="#ff0" NavBarColor="#f0b210" />, document.body);
You Can call the Navigation bar component and giving the props like this
let inputProps={
color:"blue",
title:"Title"
};
<NavigationBar props={inputProps}/>
And in the declaration of NavigationBar you can use it like this
const NavigationBar = (props)=>{
const [title,setTitle] = useState("");
const [color,setColor] = useState("");
useEffect(()=>{
setColor(props.color);
setTitle(props.title);
},[props.color,props.title]);
return(
<NavBar
title={title}
tintColor={color}
leftButton={<Button style={styles.menuButton}></Button>}
rightButton={
<Button style={styles.menuButton}></Button>}
/>
);
}
As your the color and the title changes the effect hook will trigger and update the state of the title and color using the state hook which will force the component to re-render with updated values.So its one way binding giving you a flavour of two way binding.
The render is a non-parametrised function means it does not take any parameter. So to pass parameters/value from one component to other in React Native we use props. The props is a JavaScript object that has property to pass on from one component to others.
So, you need to pass the values with props.

Categories