I Have a component that receives children and renders those children
component = React.createClass({
...some JSX
{this.props.children}
...some JSX
)}
At some point I need to loop around the children and call a method that they expose. However, children are not object instances, but rather React representations.
In essence I want to do something like this:
var children = this.props.children
React.Children.forEach(children, function(child) {
child.someAction()
})
What's the best way of achieving this?
There is one way that I came across on IRC and it involved cloning the children then accessing them by ref. Though it seems kind of convoluted.
// in JSX
{
newChildren = React.Children.map(children, function(child) {
return React.CloneElement(child, {ref: child.ref})
})
}
Then do
childRefs = this.props.children.map(function(child) {
return child.ref
})
var self = this;
childRefs.forEach(function(ref){
self.refs[ref].someAction()
})
Though, this feels like I'm working against React.
In generel you should always try to use to outside-in approach and think of data as immutable.
Providing all children with a shouldValidate flag as a prop and then make all children implement the componentWillReceiveProps function to do the validation, would be the preferred way as I see it.
Components should in my opinion never expose functions/properties to be called from outside as this goes against the idea of React.
An jsx example:
Parent
render: function() {
return this.children.map(Child => {
return <Child shouldValidate={this.state.submitting}/>
});
}
onSubmit: function() {
this.setState({
submitting: true
})
}
Child
componentWillReceiveProps: (nextProps) => {
if (this.props.shouldValidate) {
validate();
}
}
Related
I'd like to create a wrapper component to handle Outside Clicks of components.
There are two things I'm trying to avoid, which may not be possible.
Avoid creating an almost redundant <div /> to handle the ref to my <OutsideClick /> component.
Avoid having to handle this.props.passedInRef each time I want to wrap a component.
The cleanest implementation i can get thusfar is using a HOC as follows. You'll see I also tried to use React.cloneElement() but I've left if commented out.
const onOutsideClick = (Component) => {
class OnOutsideClick extends React.PureComponent {
capturedEvents = ['touchend', 'click'];
componentDidMount() {
this.capturedEvents.forEach((event) => {
document.addEventListener(event, this.handleOutsideClick, true);
});
}
componentWillUnmount() {
this.capturedEvents.forEach((event) => {
document.removeEventListener(event, this.handleOutsideClick, true);
});
}
handleOutsideClick = (e) => {
if (!this.wrapperRef.contains(e.target)) {
console.log('handled Outside Click');
}
}
setWrapperRef = (node) => {
this.wrapperRef = node;
}
// render() {
// return React.cloneElement(this.props.children, {
// ref: this.setWrapperRef,
// });
// }
render() {
return <Component ref={this.setWrapperRef} {...this.props} />;
}
}
return OnOutsideClick;
};
This gets the following error on click: _this.wrapperRef.contains is not a function at HTMLDocument.OnOutsideClick._this.handleOutsideClick.
I can get it working if I change the render method to:
render() {
return <Component forwardRef={this.setWrapperRef} {...this.props} />;
}
and inside Descendant Component render method I must define:
<div ref={this.props.forwardRef}>
...
</div>
This feels like I'm dirtying a descendant. Is it possible to pass the ref to the descendant simply because it's a descendant?
There are multiple ways to handle it.
First: As you suggested, you can pass on the ref as a prop with different name and then attach it to the elements div
Second: you can make use of forwardRef api to forward the ref to the component which would look like
const MyComponent = React.forwardRef((props, ref) => (
<div ref={ref}>{/* content here */}</div>
));
Third: you can do avoid, forwarding ref altogether by using React.findDOMNode. However, its not a recommended way and you must forward the refs as much as possible
handleOutsideClick = (e) => {
if (!ReactDOM.findDOMNode(this.wrapperRef).contains(e.target)) {
console.log('handled Outside Click');
}
}
I wrote a component which takes as children other components and passes to them additional props.
Inside the render method of my component I have code like this
React.Children.forEach(children, child => {
if (child.type === DataTableHeader)) {
header = React.cloneElement(child, {
filter, onFilterChange,
sort, onSortChange
})
}
})
where DataTableHeader is another component.
The problem is that child.type is never equal to DataTableHeader.
In chrome debugger I see that child.type reference DataTableHeader from here
(function(factory,instantiate
/**/) {
return function DataTableHeader() {
return instantiate(factory, this, arguments);
}
})
whereas DataTableHeader points to my component.
The render method comes from DataTable component, and I use it like this:
<DataTable
onFetchData={onFetchData}>
<DataTableHeader>
<DataTableHeaderCell sortField='name'>
Name
</DataTableHeaderCell>
</DataTableHeader>
{children}
</DataTable>
The problem is caused by react-hot-loader https://github.com/gaearon/react-hot-loader/issues/304
As a workaround I define static field on DataTableHeader and use it instead of comparing type type
I have a few dozen fields in my users' profiles, and I'm trying to build an efficient means of displaying them in the appropriate input form components.
eg, a profile might look like:
profile1={
name: 'Cornelius Talmadge',
phone: '1'
}
And if I could stack components in something like this...
export const FieldCatalogue = {
name: <TextField defaultValue={this.state.userProfile.name} label={"Name"} />
}
...then I could do something like this:
for (let field in Object.keys(profile1)) {
return FieldCatalogue[field]
}
which would be super cool.
This question has a great answer for if my input components were all constructed via the normal syntax (eg, Component = React.createClass({..})), but:
a. that's a lot of boilerplate
b. i'd have to pass props galore to each one, which isn't optimal
The perfect situation for me would basically be passing input components almost as strings, such that they fell into the scope (state, props, etc.) of whatever parent component they were rendered.
Is this possible and/or advisable?
The perfect situation for me would basically be passing input
components almost as strings, such that they fell into the scope
(state, props, etc.) of whatever parent component they were rendered.
Is this possible and/or advisable?
Yes, it is actually possible! No, I don't think this is something I would actually use. But it works, and it looks kinda cool. In my example, the FieldCatalogue components obviously don't have their own separate this.state object, but by binding them to the parent component this they automagically inherit the correct context.
Note that the example will not work if the components are defined as arrow functions, because arrow functions never have own this objects.
Oh, and the key in <Tmp key={i} /> is just there because we need to supply React with some kind of identifier when we loop over an array.
I had to try this as an exercise, and this is quite neat:
https://jsfiddle.net/dannyjolie/e9s09xrm/
const FieldCatalogue = {
name: function() {
return <input defaultValue = {this.state.userProfile.name} label = {"Name"}/>;
},
age: function() {
return <input defaultValue = {this.state.userProfile.age}/>;
}
}
const App = React.createClass({
getInitialState: function() {
return {
userProfile: {
name: 'Name in parent state',
age: 'Age in parent state'
}
};
},
render: function() {
let content = Object.keys(FieldCatalogue).map((objkey, i) => {
let Tmp = FieldCatalogue[objkey].bind(this)
return <Tmp key = {i} />;
});
return <div>{content}</div>);
}
});
This way the thiscontext of the parent component is passed to the FieldCatalogue components, and it just works. Really not sure if it's a good thing to do though.
You could map over the user objects keys and match them to corresponding components in fieldCatalogue. If the key exists, render the component at that key and pass all of the parent components props in:
CodePen
const fieldCatalogue = { // stateless component functions rather than React element literals
name(props) {
return <div>Name: {props.value}</div>
},
age(props) {
return <div>Age: {props.value}</div>
}
};
class ProfileFields extends React.Component {
render() {
const {userProfile} = this.props;
return (
<div>
{Object.keys(userProfile).map((key) => {
if(fieldCatalogue.hasOwnProperty(key)) {
const FieldComponent = fieldCatalogue[key];
return (<FieldComponent {...this.props} value={userProfile[key]}/>);
} else {
return null;
}
})}
</div>
);
}
}
Alternatively, your field components could just handle undefined values by returning null. Then you could render all of them and only the ones that exist on the user object would be rendered.
Given the following, is it possible to access the parent context rather than the containers from a child (non-react component) element?
The example logs container, ideally it would log parent. I would like for Parent to be self contained, not to have it's state managed by its container.
var Container = React.createClass({
getInitialState: function () {
return {
context: 'container'
}
},
render: function () {
return (
<Parent>
<a href="#" onClick={function () {console.log(this.state.context);}.bind(this)}>click me</a>
</Parent>
);
}
});
var Parent= React.createClass({
getInitialState: function () {
return {
context: 'parent'
}
},
render: function () {
return (
<div>
{this.props.children}
</div>
);
}
});
If there is another pattern for handling this, please share as well.
Note: To be clear, I understand how the this keyword works and why the above example works as it does. The example is simply meant to illustrate the problem.
You can import some React helpers for that:
var React = require('react')
...
var children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
context: this.state.context
})
})
render() {
return <div>{ children }</div>
}
...
Then your child component will have this.props.context which will be the string 'parent', but this must be a React component, as this needs to refer to the component using the parent prop
var YourComponent = React.createClass({
render() {
return (
<a href="#" onClick={() => console.log(this.props.context)}>
click me
</a>
)
}
})
------
var Parent = require('./Parent')
var YourComponent = require('./YourComponent')
...
render() {
return <Parent><YourComponent /></Parent>
}
I do not know about the first part of your question, but since you commented about dynamically creating components, here's how I do it:
You can set a state variable in the constructor of the class and its parent:
if (typeof this.state == 'undefined') {
this.state = {
componentsToRender: <div></div>
};
}
Then in the parent component, in the componentDidMount() function:
var componentsToRender = [];
if ([conditional]) {
// some logic so you know which component to render
componentsToRender.push(<customChildComponentToRender key={} />);
}
else {
componentsToRender.push(<otherComponentToRender key={} />);
}
this.setState({
componentsToRender: <div>{componentsToRender}</div>
});
Make sure to put a key (lines 4 and 7 of the second code block) or React will scream at you.
In response to your initial question, I would watch this video from the ReactJS Conference 2015 to get more of the heart behind a container. After hearing what the guys at Facebook say (who have radical views on containers!), you might want to rethink the design to make your container more of a data layer.
I would check out THIS article from the react website. I think it might give you some intuition on solving your problem.
As a general rule of thumb, I try and only use this.state to handle internal UI state of a specific component. Everything else is passed via props. If you're needing the full context of a component, I would either pass it as a prop or checkout something like flux or redux which will help you manage state between components.
I am trying to access the methods of a children in the parent Component.
First I wanted to use refs instead of this.props.children, but refs are only accessable when using them IN my component.
When using this, it seems not to be possible:
<Parent>
<Child ref="testChild" />
</Parent>
In my Parent Component I am not able to access this.refs.testChild - because of this I have to access this component with this.props.children.
However: When accessing them with this.props.children I am not able to call methods of the child.
Example:
// Child.jsx
{
customMethod() {},
render() {... some stuff ...}
}
// Parent.jsx
{
callChildrenMethods() {
this.props.children.map((child)=>{
console.log(child.props); // Props Object
console.log(child.customMethod); // Undefined
});
},
render() {return(<div>{this.props.children}</div>)}
}
As you can see: The customMethod is undefined. Is there any simple way to access the methods ? The better way would be to access the children with refs but this is not possible in my case.
this.props.children is opaque, you should iterate using React.Children API. Anyways, here's a fiddle which uses some hackery to invoke the child methods -
https://jsfiddle.net/sukantgujar/4bffu7tw/3/
Basically you need to access the type object of the child which points to the wrapped component instance.
var child = React.Children.only(this.props.children),
childType = child.type,
childProto = child.type.prototype,
childName = childProto.constructor.displayName,
childMethod = childProto.someMethod;
You should let the child know through props:
var Child = React.createClass({
render: function() {
if(this.props.shouldRunChildrensMethod) {
this.childsMethod();
}
return (...);
}
});
var Parent = React.createClass({
getInitialState: function() {
return {
shouldRunChildrensMethod: false
}
},
callChildrenMethods: function() {
this.setState({
shouldRunChildrensMethod: true
});
},
render: function() {
return (
<Child shouldRunChildrensMethod={this.state.shouldRunChildrensMethod} />
);
}
});
You can also view 2 way binding:
https://facebook.github.io/react/tips/communicate-between-components.html
Though this communication will be individually 1 by 1 with each child to parent.