React: How to access refs in this.props.children - javascript

I want to call a function of a child component.
Is there a possibility to get refs from this.props.children in React.
var ComponentSection = React.createClass({
componentDidMount: function() {
// How to access refs in this.props.children ?
this.refs.inner.refs.specificPanel.resize();
},
render: function () {
return (
<div className="component-section" ref="inner">
{this.props.children}
</div>
);
}
});
var Panel = React.createClass({
resize: function() {
console.log('Panel resizing');
},
render: function () {
return (
<div className="Panel">
Lorem ipsum dolor sit amet
</div>
);
}
});
var MainComponent = React.createClass({
render: function () {
return (
<ComponentSection>
<Panel ref="specificPanel"></Panel>
</ComponentSection>
);
}
});
ReactDOM.render(
<MainComponent></MainComponent>,
document.getElementById('container')
);
I made a little demo: https://jsfiddle.net/69z2wepo/26929/
Thanks in advance

Ref in React is a relationship between a component and its owner while what you want is a relationship between an element and its parent. A parent-child relationship is opaque, the parent only receives the child elements on render and never gets access to the resolved components.
In your case, the ref="specificPanel" establishes a link from the MainComponent to the Panel. If you want to call methods of Panel from ComponentSection, it should own the Panels instead of receiving them as children.
You could always get creative with React.Children.map and React.createElement (cloning the elements by hand and thus stealing them from the original owner), but this introduces some serious potential pitfalls. A better approach would likely be re-thinking the ownership structure of your component tree.

You are trying to set a ref on a <div> instead of a React component.
You could also refactor your code so that only <ComponentSection> needs to know about the <Panel> component, and render it in it's render function.
var ComponentSection = React.createClass({
componentDidMount: function() {
this.refs.inner.resize();
},
render: function() {
return (
<div className="component-section">
<Panel ref="inner"/>
</div>
);
}
});
var MainComponent = React.createClass({
render: function() {
return (
<ComponentSection />
);
}
});
Here is a working JSFiddlle.

Related

Reactjs - parent, child action and functions

I am working on a reactjs application - and I am breaking up a big component to have a child component. I've created a callback function in the child that will go back to the parent. When a checkbox is checked -- the child component does the callback and this goes back into the parent shell -- however I want to now jump out of the event callback and push the data to an original parent function.
my application kind of looks like this on a streamlined level.
var Parent = React.createClass({
onSelect: function(value, flag){
this.updateSelected(value, flag);
}
updateSelected: function(value, flag) {
let array = this.state.selectedArray;
array.push({"value": value, "flag": flag});
this.setState({
selectedArray: array
});
},
render: function() {
return (
<div>
<Child onSelect={this.changeHandler} />
<span>{this.state.value}</span>
</div>
);
}
});
var Child = React.createClass({
selectHandler: function(e) {
this.props.onSelect(e.target.value, false);
},
render: function() {
return (
<input type="checkbox" onSelect={this.selectHandler} />
);
}
});
but I can not just invoke the "this.updateSelected(value, flag);" as its inside the this scope.
You should use ES6 syntax and JSX syntax instead of pure React API, that help you a lot to reduce this kind of error about scoping and become your code more readable, but If you even want to use that syntax, so you should bind the function once you pass down to the child component , you can do it like this:
var Parent = React.createClass({
onSelect: function(value, flag){
this.updateSelected(value, flag);
}
updateSelected: function(value, flag) {
let array = this.state.selectedArray;
array.push({"value": value, "flag": flag});
this.setState({
selectedArray: array
});
},
render: function() {
return (
<div>
<Child onSelect={this.changeHandler.bind(this)} />
<span>{this.state.value}</span>
</div>
);
}
});
as you can see adding the .bind method and passing as argument the context that you want your function execute, then once your function be invoked, the scope of that function will be the parent component instead of child component.
Remember that this approach bind method could affect the improve of your component if your Parent component rerenders many times.
If you are using JSX syntax you should do something like
class YourComponent extends React.Component {
changeHandler = (value, flag) => {
this.updateSelected(value, flag);
}
render() {
return (
<div>
...
<Child onSelect={this.changeHandler} />
</div>
);
}
}

react-redux get width of component's parent div

This is part of the component :
import MyComp from '../../lib/MyComp'
const Data = ( { data } ) => (
<div className="data-box" id="data-box">
<MyComp data={data} />
</div>
)
How do I get the width of the data-box div inside MyComp container?
Check this working demo: JSFiddle:
var Parent = React.createClass({
render: function() {
return <div id="parent">Hello Parent<Child></Child></div>;
}
});
var Child = React.createClass({
componentDidMount: function() {
alert('Parent width: ' + this.refs.child.parentNode.clientWidth);
},
render: function() {
return <div ref="child">Hello Child</div>;
}
});
Stating ref="child" will make the element accessable by the component itself, through this.refs.child. It is the vallina node instance. Using this.refs.child.parentNode.clientWidth will return the parent's width. Or, use this.refs.child.parentNode.getBoundingClientRect().
Reference: React refs
You need to use react refs.
on your MyComp class:
class MyComp extends React.Component {
//All your PropTypes and functions...
//New function
newFunction () {
console.log(this.refs.refName);
//This will give you the Data component. There you can call methods to calculate width, or whatever you need to do with that component
}
//Your render function
render() {
return <div ...whatever you have... ref="refName">
}
}
You can check react documentation
what should work is something like this
MyComp could look like this
render() {
return <div ref="node"></div>
}
with this.refs.node you get the current dom element and with
this.res.node.parentNode
you should get the parentNode

Reusing react component

I'm with some questions on the use of component react.
Basically I want to use the "Title" component in various parts of the code, but it always has "states" that vary. I do not quite understand the official documentation on this issue, as I do to inherit this component, and only change the "states" for what I want?
I know the question seems silly, but I'm learning and React is very different from everything I saw.
var Title = React.createClass({
displayName: "Title",
getDefaultProps: function () {
return {
className: ""
}
},
render: function () {
return <h1 className={this.props.className}>{this.state.content}</h1>
}
});
You should use props rather than state in this instance. Props are owned by parents, and state is owned by the component itself. Since you would like to use this component in multiple places, then naturally a parent will decide what the text of the Title should be - therefore props are what you want.
React has a special prop children, which takes on the value of whatever is passed inside a component's JSX tag.
For instance, here's a mock component which uses your Title component multiple times:
var MyComponent = React.createClass({
render: function() {
return (
<div>
<Title className="foo">Hello</Title>
<Title className="bar">World</Title>
</div>
);
}
});
Since you are now passing text to the component as the children prop, you must update you Title component to render this:
var Title = React.createClass({
displayName: "Title",
getDefaultProps: function () {
return {
className: ""
}
},
render: function () {
// NOTE: we are now using children prop
return <h1 className={this.props.className}>{this.props.children}</h1>
}
});
A side benefit of this is that you can build more complex titles, containing multiple children, and it will just work:
var MyOtherComponent = React.createClass({
render: function() {
return (
<div>
<Title className="foo">
<span>Hello</span>
World
</Title>
</div>
);
}
});
You might want to replace {this.state.content} by {this.props.children} and use your component like this :
<Title className="myclass">my title</Title>
As a general rule, try to avoid using state when possible, dumb component are more easily reusable.

react.js: removing a component

I'm fairly new at react.js, so any help is greatly appreciated.
I have this: https://jsfiddle.net/rzjyhf91/
Wherein I have made 2 components: an image and a button.
The goal is to remove the image with a click of the button, I use unmountComponentAtNode for that, but it does not work:
var App = React.createClass({
render: function() {
return (
<div><MyImage /><RemoveImageButton /></div>
);
}
});
var MyImage = React.createClass({
render: function() {
return (
<img id="kitten" src={'http://placekitten.com/g/200/300'} />
);
}
});
var RemoveImageButton = React.createClass ({
render: function() {
return (
<button onClick={this.handleClick}>remove image</button>
)
},
handleClick: function(){
React.unmountComponentAtNode(document.getElementById('kitten'));
}
});
React.render(<App />, document.body);
How can I remove a react component from another component?
Well, it seems you should rethink how the display control is handled. React is all about isolated components, and so, you shouldn't be unmounting a component that is mounted by a parent component. Instead, you should use a callback passed down through props to accomplish something like that.
Your actual implementation will depend on your use case, but an updated version of your example that works is at: https://jsfiddle.net/nt99zzmp/1/
var App = React.createClass({
render: function() {
var img = this.state.showImage ? <MyImage /> : '';
return (
<div>{img}<RemoveImageButton clickHandler={this.removeImage} /></div>
);
},
getInitialState: function() {
return {
showImage: true
};
},
removeImage: function() {
this.setState({ showImage: false });
}
});
var MyImage = React.createClass({
render: function() {
return (
<img id="kitten" src={'http://placekitten.com/g/200/300'} />
);
}
});
var RemoveImageButton = React.createClass ({
render: function() {
return (
<button onClick={this.props.clickHandler}>remove image</button>
)
}
});
React.render(<App />, document.body);
Basically removing a component doesn't make sense in React, you probably still thinking jQuery ways, basically in all modern and new JavaScript libraries including React, you should manage your component using state or a route to handle these things, deleting an element or component is not a good way to do these things in React or Angular for example.
For example you can have a boolean in this case and if it's true, show your image, otherwise hide it, or even return a different element in your component.
So in this case, you have a component which will return differently depends on props or state... something like this:
////
var MyImage = React.createClass({
render: function() {
if(this.state.showImage) {
return (
<img id="kitten" src={'http://placekitten.com/g/200/300'} />
);
} else {
return<p>no image!</p>;
}
}
});
////
In this example, if you set this.state.render = false, the component will be removed from DOM:
render() {
const { render } = this.state;
if (render === false) return null;
return (<p>I am here as long as render isn't false</p>);
}

Set inline style of children

I’m trying to set some inline styles to children in React. I have this that actually works, sort of:
var App = React.createClass({
render: function() {
var children = React.Children.map(this.props.children, function(child, i) {
child.props.style.width = '100px'
})
return (
<div>{children}</div>
)
}
})
BUT, if I try to change styles when state changes, the styles will remain the same. You can easily see this when trying something like this: (fiddle: http://jsfiddle.net/9UvWL/)
var World = React.createClass({
getInitialState: function() {
return { width: 0 }
},
componentDidMount: function() {
this.setState({ width: 100 })
},
componentDidUpdate: function() {
console.log(this.getDOMNode().innerHTML);
},
render: function() {
var width
var children = React.Children.map(this.props.children, function(child, i) {
width = i*this.state.width
console.log('Setting width: '+width);
child.props.style = {width: (i*this.state.width)+'px'}
return child
}, this)
return <div>{children}</div>
}
})
var Hello = React.createClass({
render: function() {
return (
<World>
<div>1</div>
<div>1</div>
</World>
)
}
})
React.renderComponent(<Hello />, document.body);
It will log that width has changed, but the props.style is not working as expected. And using child.setProps() will throw "Invariant Violation: replaceProps(...): Can only update a mounted component.". Is there another "react-way" of changing inline styles to children?
You are modifying the props object of each child directly, which bypasses parts of React's update mechanisms. We will be making this impossible soon.
In this case you want to use cloneWithProps inside your World component's render function.
var children = React.Children.map(this.props.children, function(child, i) {
width = i*this.state.width;
console.log('Setting width: '+width);
return React.addons.cloneWithProps(child, {style: {width: width + 'px'}})
}, this)
return <div>{children}</div>
cloneWithProps creates a new copy of the component and merges in additional props. You should use this pattern to ensure you're giving React all the hints it needs to know when to update.
Here's your example working: http://jsfiddle.net/zpao/Tc5Qd/
Just after the "Can only update a mounted component" check, there is another more descriptive error message:
invariant(
this._mountDepth === 0,
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
'component with a parent. This is an anti-pattern since props will ' +
'get reactively updated when rendered. Instead, change the owner\'s ' +
'`render` method to pass the correct value as props to the component ' +
'where it is created.'
);
The logic comes from a different perspective than updating DOM nodes. When a DOM node needs to be changed, the way to do that normally is to get a reference to the DOM node, then set its properties. With React, instead of updating particular nodes, the render methods just return new React components with the correct properties set, then the back-end code works out which DOM nodes to update.
When something is shown wrongly in normal DOM code, it is hard to know where the problem is, since any code can update the DOM nodes.
When the code is arranged into render methods like in React, the render methods always take in this.props and this.state and return entire, fully rendered, components. Then, if something isn't rendered properly, it is always possible to look in the render method to find the problem, since that is the only place where that render happens. (And this works whether it is the first render or a second render, so there doesn't need to be a distinction between rendering components the first time, and updating components.)
So, the way around the problem you're describing, is to move the logic about rendering the divs into the render method which creates the divs, rather than in the render method of a different component. Something like this:
var World = React.createClass({
render: function() {
return <div>{this.props.children}</div>;
}
});
var Hello = React.createClass({
getInitialState: function() {
return { width: 0 };
},
componentDidMount: function() {
this.setState({ width: 100 });
},
render: function() {
var children = ["a", "b", "c"].map(function(content, i) {
return (
<div style={{ width: i * this.state.width }}>
{content}
</div>
);
}, this);
return (
<World>
{children}
</World>
)
}
});
React.renderComponent(<Hello />, document.body);
http://jsfiddle.net/kN4DV/2/

Categories