I'm having a bit of a trouble getting my head around how i would communicate between my parent and child components in my specific use case.
I have a child component that renders some points using an external lib.
What I'm currently doing is implementing componentDidMount/Update and use the external lib to render the points in this.props.points (which is an array provided by the parent component).
Rendering the points involves looping through them and call something like ExternalLib.addPoint(point).
What i would like to do, instead of looping through all the points everytime this.props.points changes, is making the parent component add (or remove) individual points to the child component.
Is it React friendly to call something like this.refs.myChild.addPoint(point) in the parent component?
Are there other techniques to achieve something like this?
Update
Here's some code (https://jsfiddle.net/69z2wepo/61366/):
const ExternalLib = {
addPoint: function(el, point) {
var li = document.createElement("li");
li.innerHTML = point;
el.appendChild(li);
}
}
class Child extends React.Component {
componentDidMount() {
this.renderPoints();
}
componentDidUpdate() {
this.renderPoints();
}
renderPoints() {
const el = this.refs.points;
el.innerHTML = '';
this.props.points.forEach(function(point) {
ExternalLib.addPoint(el, point);
});
}
render() {
console.log('render')
return (
<div>
<ul ref="points"></ul>
</div>
);
}
}
class Parent extends React.Component {
constructor() {
super();
this.state = {
points: []
};
}
addPoint() {
const points = this.state.points.slice();
points.push((new Date()).getTime());
this.setState({
points: points
});
}
render() {
return (
<div>
<button onClick={this.addPoint.bind(this)}>Add Point</button>
<Child points={this.state.points} />
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('container')
);
This is simplified because in this example I could generate the markup directly making use of map and leveraging React's partial DOM update - the external lib does some extra stuff that's not in this question's scope.
Thanks!
If you want to interfere with React rendering process, you can code shouldcomponentupdate.
In your children, if you make shouldcomponentupdate return false and call ExternalLib.addPoint(point), it should do the job:
shouldComponentUpdate(nextProps){
//comparePoints() is a function able to find if a new point is present in the list, comparing existing used points with new one
var newPoint = comparePoints(this.props.points, nextProps.points)
if(newPoint){
ExternalLib.addPoint(newPoint);
//forbide re-render
return false;
}
//enable other update
return true;
}
Related
I have 3 components, that is 2 child components and one parent component.
I wanted to pass the child component value to parent (only the values not the components enitirely,it should not visible in parent) and this values from parent to another child component.
Any suggestions or logic of how to proceed on this, since I don't have any guidance as of right now I had to ask here. Is the above problem possible.
The code is very complex, so I have not put here.
Thank you
When you say values, do you mean state, props, user input, something else?
If you mean state or props: React has a 1-way data flow, so the easiest way to accomplish this is to actually store the data at a higher level. Either store the data used by the child in the parent and pass it down to the children for consumption, or else use a store that both parent and children have access to. Either way, this will make it much easier for all components to access the data.
If you mean user input: one way you can accomplish this is to pass a callback from the parent component to the child as a prop, and then in the child call that callback when a user does something or changes some value. The callback function can make the data accessible to the parent on that user action, and then you can decide what to do with the data from there.
AksharaDL,
Child to Parent — Use a callback and states
Parent to Child — Use Prop
Also here is another article explaining it: https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
here is the solution.
in parrent component you have a state. and have a setData method to update state. pass setData to ChildOne use props. and data to ChilTwo and use it
class StoreBanner extends React.Component {
constructor() {
this.state = {
data: 'whatever'
}
}
setData = (data) => {
this.setState({data})
}
render() {
return (
<div>
<ChildOne setData={this.setData}/>
<ChildTwo data={this.state.data}/>
</div>
)
}
}
and in ChildOne you can update the parrent state
class ChildOne extends React.Component {
setParentData = () => {
this.props.setData('another')
}
...
}
You can do it like this.
class ParentComp extends React.Component {
constructor() {
super();
this.state = {
theStateToPass: null
}
this.receiveDataFromChild = this.receiveDataFromChild.bind(this);
}
receiveDataFromChild(data) {
this.setState({
theStateToPass: data
})
}
render() {
return (
<div>
<FirstChild updateMe={this.receiveDataFromChild} />
<SecondChild stateFromFirstChild={this.state.theStateToPass} />
</div>
)
}
}
class FirstChild extends React.Component {
constructor(props) {
super(props);
this.callParentMethod = this.callParentMethod.bind(this);
}
callParentMethod(e) {
let someDataToSend = "ABC";
this.props.updateMe(someDataToSend)
}
render() {
return (
<div onClick={this.callParentMethod}>
</div>
)
}
}
class SecondChild extends React.Component {
render() {
return (
<div>
{this.props.stateFromFirstChild}
</div>
)
}
}
however it becomes complex and might lead to one's pulling their hair out. so i would suggest using redux , it keeps the flow simple , you have a reducer , actions and a container. everything goes with a flow and keeps it clean but it does comes with an extra overhead of more code as you will be creating container reducer and actions.
I'm fairly very new to ReactJS, JavaScript and whole coding thing.
I have made a list of array that is being passed on to child from parent component, now I want to make a onClick event that whenever user click on any of that list item.
The value (used as key id) is sent back to the parent, so the parent can send that key value to another child which will use conditional rendering based on that key to which component to render. I preferably don't want to use any other library like flux.
I am stuck as I can;t pass that value from child back to parent. There is some confusion about syntax I think or maybe my logic is not correct. I have only able to bind it to a function within the child class to check if the click is being registered.
class Parent extends React.Component {
render() {
var rows = [];
this.props.listlinkarray.forEach(function(linklist) {
rows.push(<Child linklist={linklist} key={linklist.key} />);
});
return (
<div>
<h3> ListHead </h3>
{rows}
</div>
);
}}
class Child extends React.Component {
// was checking how the Onclick event works //
getComponent(data,event) {
console.log('li item clicked!');
console.log(data);
event.currentTarget.style.backgroundColor = '#ccc';
}
render() {
return (
<div>
<ul><li onClick={this.getComponent.bind(this,this.props.linklist.key)}>
</div>
);
}
}
Array has this data
var ListNameAndLinks= [
{ name:'ABC', key:1} ,
{ name:'BCD', key:2}
];
I want to pass the value the parent get from child based on click to this class so it can render based on that value it get.
class Check extends React.Component {
render() {
var i = 1
// i want that value from parent to this component in if condition
if (i=1) {
return <UIone />;
}
return <UItwo />;
}
}
Pass a onClick method from parent component, like this:
this.props.listlinkarray.forEach((linklist)=>{
rows.push(<Child linklist={linklist} key={linklist.key} onClick={this.onClick.bind(this)}/>);
});
Define this function in Parent:
onClick(key){
console.log(key);
}
In Child Component call this method and pass the id back to parent, like this:
getComponent(key) {
this.props.onClick(key);
}
It will work.
Check the jsfiddle for working example: https://jsfiddle.net/ahdqq5yy/
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 know it's not a functional approach to be able to do something like this.parent in a React component, and I can't seem to find any properties on a React component instance that lead to the parent, but I'm just looking to be able to do some custom things where I need this.
Before anyone wastes their time explaining it's not the functional React "way," understand that I need this because of the following I'm trying to achieve:
Build a transpiler for Meteor's Spacebars templating engine, whose rendering model does take into consideration parent components/templates.
I've already built a transpiler that modifies the output jsx to achieve this. I do this by passing in parent={this} in all child components composed. However, after the fact it occurred to me that maybe I simply don't know of something that will give me a way to access the parent component instance without additional transpilation modifications.
Any tips would be much appreciated.
There's nothing wrong if you need to access the parent's props and functions from the children.
The point is that you should never use React internals and undocumented APIs.
First of all, they are likely to change (breaking your code) and, most importantly, there are many other approaches which are cleaner.
Passing props to children
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
const Child = ({ fn }) => <button onClick={fn}>Click me!</button>
Working example
Using context (if there's no direct parent/child relation)
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
getChildContext() {
return {
fn: this.fn,
}
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
Parent.childContextTypes = {
fn: React.PropTypes.func,
}
const Child = (props, { fn }) => <button onClick={fn}>Click me!</button>
Child.contextTypes = {
fn: React.PropTypes.func,
}
Working example
Update for React 0.13 and newer
Component._owner was deprecated in React 0.13, and _currentElement no longer exists as a key in this._reactInternalInstance. Therefore, using the solution below throws Uncaught TypeError: Cannot read property '_owner' of undefined.
The alternative is, as of React 16, this._reactInternalFiber._debugOwner.stateNode.
You've already recognized that this is not a good thing to do almost always, but I'm repeating it here for people that don't read the question very well: this is generally an improper way to get things done in React.
There's nothing in the public API that will allow you to get what you want. You may be able to get to this using the React internals, but because it's a private API it's liable to break at any time.
I repeat: you should almost certainly not use this in any sort of production code.
That said, you can get the internal instance of the current component using this. _reactInternalInstance. In there, you can get access to the element via the _currentElement property, and then the owner instance via _owner._instance.
Here's an example:
var Parent = React.createClass({
render() {
return <Child v="test" />;
},
doAThing() {
console.log("I'm the parent, doing a thing.", this.props.testing);
}
});
var Child = React.createClass({
render() {
return <button onClick={this.onClick}>{this.props.v}</button>
},
onClick() {
var parent = this._reactInternalInstance._currentElement._owner._instance;
console.log("parent:", parent);
parent.doAThing();
}
});
ReactDOM.render(<Parent testing={true} />, container);
And here's a working JSFiddle example: http://jsfiddle.net/BinaryMuse/j8uaq85e/
Tested with React 16
I was playing around with something similar using context, tho to anyone reading this, for most usual cases, accessing the parent is not advised!
I created a holder that when used, would always have a reference to the first holder up the display list, so its 'parent' if you will. Looked something like this:
const ParentContext = React.createContext(null);
// function to apply to your react component class
export default function createParentTracker(componentClass){
class Holder extends React.PureComponent {
refToInstance
render(){
return(
<ParentContext.Consumer>
{parent => {
console.log('I am:', this, ' my parent is:',parent ? parent.name : 'null');
return(
<ParentContext.Provider value={this}>
<componentClass ref={inst=>refToInstance=inst} parent={parent} {...this.props} />
</ParentContext.Provider>
)}
}
</ ParentContext.Consumer>
)
}
}
// return wrapped component to be exported in place of yours
return Holder;
}
Then to use it you would pass your react component to the method when you export it like so:
class MyComponent extends React.Component {
_doSomethingWithParent(){
console.log(this.props.parent); // holder
console.log(this.props.parent.refToInstance); // component
}
}
// export wrapped component instead of your own
export default createParentTracker(MyComponent);
This way any component exporting the function will get its parent's holder passed in as a prop (or null if nothing is further up the hierarchy). From there you can grab the refToInstance. It will be undefined until everything is mounted though.
I'm using react-bootstrap's ModalTrigger to show a field-heavy modal (based on react-bootstrap's Modal), which means sending it a bunch of props:
<ModalTrigger modal={<MyModal field1={value1} field2={value2} (more fields...)/>}>
Click here to open
</ModalTrigger>
The parent component that creates the trigger has the fields/values passed in via props, and the parent component of that component has it passed as props as well, by the top-level component that actually holds the data. Both are basically pipes, which is a classic childContext scenario, except that it doesn't work. Here's a simplified version of what I've tried:
var MyModal = React.createClass({
contextTypes : {foo : React.PropTypes.string},
render : function() {
return (
<Modal {...this.props} title="MyTitle">
<div className="modal-body">
The context is {this.context.foo}
</div>
</Modal>
);
}
});
var Content = React.createClass({
childContextTypes : {foo: React.PropTypes.string},
getChildContext : function() {return {foo : "bar"}},
render : function() {
return (
<ModalTrigger modal={<MyModal/>}>
<span>Show modal</span>
</ModalTrigger>
)
}
});
The modal pops up with "The context is", without showing the actual context.
I believe this is happening because the prop sent to ModalTrigger is already rendered/mounted somehow, but I'm not sure why. To the best of my understanding, the owner of MyModal is the Content component, which means that the context should be ok, but this is not the case.
Some more information: I already tried passing {...this.props} and context={this.context} to MyModal with no success. Also, maybe relevant, ModalTrigger uses cloneElement to make sure the modal's onRequestHide prop points to the trigger's hide function.
So what am I missing here? :/
React.cloneElement will change the owner of the element when the ref prop is overriden, which means context will not be passed from the previous owner. However, this does not seem to be the case with ModalTrigger.
Note that the owner-based approach won't work altogether in React 0.14, since context will be passed from parent to child and not from owner to ownee anymore. ModalTrigger renders its modal node prop in another branch of the DOM (See OverlayMixin). Thus, your Modal component is not a child nor a descendant of your Content component and won't be passed child context from Content.
As for solving your problem, you can always create a component whose sole purpose is to pass context to its children.
var PassContext = React.createClass({
childContextTypes: {
foo: React.PropTypes.string
},
getChildContext: function() {
return this.props.context;
},
render: function() {
return <MyModal />;
},
});
To use it:
<ModalTrigger modal={<PassContext context={this.getChildContext()}/>}>
As Matt Smith hinted, it turns out that react-bootstrap already includes a very similar approach to forwarding context via ModalTrigger.withContext. This allows you to create a ModalTrigger component class which will forward its context to its modal node prop, no matter its position in the VDOM tree.
// MyModalTrigger.js
module.exports = ModalTrigger.withContext({
foo: React.PropTypes.String
});
There is a much better way of passing context on to your "portal" type components which render their children into a different container outside the React tree.
Using "renderSubtreeIntoContainer" rather than "render" will pass the context into the subtree as well.
It can be used like so:
import React, {PropTypes} from 'react';
import {
unstable_renderSubtreeIntoContainer as renderSubtreeIntoContainer,
unmountComponentAtNode
} from 'react-dom';
export default class extends React.Component {
static displayName = 'ReactPortal';
static propTypes = {
isRendered: PropTypes.bool,
children: PropTypes.node,
portalContainer: PropTypes.node
};
static defaultProps = {
isRendered: true
};
state = {
mountNode: null
};
componentDidMount() {
if (this.props.isRendered) {
this._renderPortal();
}
}
componentDidUpdate(prevProps) {
if (prevProps.isRendered && !this.props.isRendered ||
(prevProps.portalContainer !== this.props.portalContainer &&
prevProps.isRendered)) {
this._unrenderPortal();
}
if (this.props.isRendered) {
this._renderPortal();
}
}
componentWillUnmount() {
this._unrenderPortal();
}
_getMountNode = () => {
if (!this.state.mountNode) {
const portalContainer = this.props.portalContainer || document.body;
const mountNode = document.createElement('div');
portalContainer.appendChild(mountNode);
this.setState({
mountNode
});
return mountNode;
}
return this.state.mountNode;
};
_renderPortal = () => {
const mountNode = this._getMountNode();
renderSubtreeIntoContainer(
this,
(
<div>
{this.props.children}
</div>
),
mountNode,
);
};
_unrenderPortal = () => {
if (this.state.mountNode) {
unmountComponentAtNode(this.state.mountNode);
this.state.mountNode.parentElement.removeChild(this.state.mountNode);
this.setState({
mountNode: null
});
}
};
render() {
return null;
}
};
This is an example of a portal I use in my production app Casalova that render context properly into their children.
Note: this API is undocumented and is likely to change in the future. For now, though, it's the right way to render context into portal components.