I have a component which is updated by a parent component by passing a prop. Within the componentWillReceiveProps i would like to change a state (availableData) which contains the newly added data from the prop (newData).
The prop is named newData, and the state which is updated is named availableData.
When i attempt to access the availableData where i concatenate new (unique) data i get following error:
Uncaught TypeError: Cannot read property 'availableData' of undefinedInline JSX script:79
And the code snippet:
var DataList = React.createClass({
getInitialState: function() {
return {availableData: []};
},
componentWillReceiveProps: function(nextProps) {
var availableData = this.state.availableData;
var newData = nextProps.newData;
if (_.isEmpty(availableData)) {
this.setState({availableData: nextProps.newData});
} else {
_.each(newData, function(_newData) {
var isDuplicate = false;
_.each(availableData, function(_availableData) {
if(isSameData(_availableData, _newData)) {
isDuplicate = true;
}
});
if (!isDuplicate) {
console.log(_newData);
this.setState({ availableData: this.state.availableData.concat([_newData]) });
}
});
}
},
handleClick: function (_data) {
},
render: function() {
var dataItems = this.state.availableData.map(function (_data, index) {
return <DataItem data={_data} key={index} onClick={this.handleClick.bind(this, _data)} />;
}, this);
return (
<div className="col-lg-3">
<ul className="list-group">
<li className="list-group-item active">Data</li>
{dataItems}
</ul>
</div>
);
}
});
Failing on:
this.setState({ availableData: this.state.availableData.concat([_newData]) });
UPDATE:
Solved by setting var _this = this; outside the loop and referring to _this, unfortunately all setStates are not being initialized.
this isn't the component in the context of your duplicate. You need to pass the outer most this to your _.each.
_.each(list, iteratee, [context])
Related
I saw some questions speaking about similar issues but somehow I still do not manage to solve my issue so here I am asking for your kind help. I am pretty new to React and would like to send a function from a Parent to a child and then use it from the Child but somehow when I want to use it it says
Uncaught TypeError: Cannot read property 'props' of undefined"
Edited Code after first answers were helping:
var Menu = React.createClass({
links : [
{key : 1, name : "help", click : this.props.changePageHelp}
],
render : function() {
var menuItem = this.links.map(function(link){
return (
<li key={link.key} className="menu-help menu-link" onClick={link.click}>{link.name}</li>
)
});
return (
<ul>
{menuItem}
</ul>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function() {
console.log('help');
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
Pass a value from Menu function and recieve it in the changePageHelp function and it works.
var Menu = React.createClass({
render : function() {
return (
<div>
{this.props.changePageHelp('Hello')}
</div>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function(help) {
return help;
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="admin"></div>
For performance reasons, you should avoid using bind or arrow functions in JSX props. This is because a copy of the event handling function is created for every instance generated by the map() function. This is explained here: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
To avoid this you can pull the repeated section into its own component. Here is a demo: http://codepen.io/PiotrBerebecki/pen/EgvjmZ The console.log() call in your parent component receives now the name of the link. You could use it for example in React Router.
var Admin = React.createClass ({
_changePageHelp : function(name) {
console.log(name);
},
render : function () {
return (
<div>
<div id="menu-admin">
<Menu changePageHelp={this._changePageHelp} />
</div>
</div>
);
}
});
var Menu = React.createClass({
getDefaultProps: function() {
return {
links: [
{key: 1, name: 'help'},
{key: 2, name: 'about'},
{key: 3, name: 'contact'}
]
};
},
render: function() {
var menuItem = this.props.links.map((link) => {
return (
<MenuItem key={link.key}
name={link.name}
changePageHelp={this.props.changePageHelp}
className="menu-help menu-link" />
);
});
return (
<ul>
{menuItem}
</ul>
);
}
});
var MenuItem = React.createClass ({
handleClick: function() {
this.props.changePageHelp(this.props.name);
},
render : function () {
return (
<li onClick={this.handleClick}>
Click me to console log in Admin component <b>{this.props.name}</b>
</li>
);
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
Possible to change onclick function like changing props, like changing 'props message' to 'new message' ?
For example:
var SmallMessageBox = React.createClass({
getDefaultProps: function() {
return {
message: 'props message',
onClick: 'this.eventHandler_Two'
}
},
eventHandler_One: function(){
console.log('event1');
},
eventHandler_Two: function(){
console.log('event2');
},
render: function(){
return (
<div>
<small>{this.props.message}</small>
<button onClick={this.eventHandler_One}>button</button>
</div>
);
}
});
React.render(
<SmallMessageBox message="new message" onClick="new onClick function for event2" />, document.getElementById('react-container'),
function(){
console.log('after render');
}
);
Yes, components can be supplied with properties of type function. You can either bind event handlers directly to functions passed through props or do something in your internal component method, before executing the prop function. Please note, that you cannot change definition of supplied function, once target component was initialized, it will not be updated.
Also, in many cases you must use bind on your passed in function to maintain proper context.
Here's how it should look like in accordance with your example:
var SmallMessageBox = React.createClass({
propTypes: {
message: React.PropTypes.string.isRequired,
onClick: React.PropTypes.func
},
getDefaultProps: function() {
return {
message: 'props message',
onClick: function() {
console.log("I will be executed, only if props.onClick was not specified");
}
}
},
eventHandler: function() {
},
render: function() {
return (
<div>
<small>{this.props.message}</small>
<button onClick={ this.props.onClick }>button</button>
</div>
);
}
});
React.render(
<SmallMessageBox onClick={function() { console.log( "remove me to get the other console.log"); }} message="new message"/>, document.getElementById('react-container'),
function(){
console.log('after render');
}
);
I would also encourage you to implicitly specify your props with their type. You can find more information here.
I want to share data with < Filtering /> component from onChange event to parent component < ViewTwoComponent /> I don't know how to do it
Do you know maybe how to share data between component and his parent
ViewTwoComponent they don't see ref value and i don't know why?
On console is error: Uncaught TypeError: Cannot read property 'getDOMNode' of undefined
var ViewTwoComponent = React.createClass({
"getInitialState": function() {
return {
"userTextValue": "hello11111111111111",
"userTextRef": "userTextRef"
}
},
"updateState": function(value) {
this.setState({userTextValue: value })
},
"handleChange": function() {
this.updateState(this.refs.userTextRef.getDOMNode().value)
},
"render": function() {
return <div>
<Inner />
<Filtering refName={this.state.userTextRef} handleChange={this.handleChange} userTextValue={this.state.userTextValue} />
</div>;
}
})
var Inner = React.createClass({
"render": function() {
return <span>INNER</span>;
}
});
var Filtering = React.createClass({
"render": function() {
return <span>
<input type="text" ref={this.props.refName} onChange={this.props.handleChange} value={this.props.userTextValue} />
</span>;
}
});
React.render(< ViewTwoComponent />, document.getElementById("inner"))
It's a bit confusing what you mean when you try to reference this.refs.userTextRef but I'm assuming you want the value that's in your state. I'm also going to assume that the value of the key userTextRef is not actually userTextRef. You could try accessing the value by using square brackets.
"handleChange": function() {
this.updateState(this.refs[userTextRef]getDOMNode().value)
}
I am new to React, but from what I remember of the tutorials, you should be passing a function of the Filtering component to the ViewTwoComponent.
var Filtering = React.createClass({
"childChanged" : function(child, value) {
console.log("child: " + child + "change value to: " value);
},
"render": function() {
return <span>
<input type="text" ref={this.props.refName} onChange={this.props.handleChange} value={this.props.userTextValue} notifyParent={this.childChanged} />
</span>;
}
});
And then, in the handleChange method of the ViewTwoComponent class, you call this.props.notifyParent(this, this.state.userTextValue);
I have a Legend, which contains multiple Legend.Items children. I'm having a problem where currently onClick it is possible to deselect all of the Legend Items which has consequences that I'd like to avoid. Is it possible to set some sort of onClick handler in the Legend component that can have some state clicked and check whether there are n - 1 legend items "selected/faded", n being the total number of legend items? I looked at the JSX Spread Attributes, but because I'm using {this.props.children}, I'm not sure how to use them or if they would work in this context.
I also took a look at this blogpost (http://jaketrent.com/post/send-props-to-children-react/), but it looked a bit hacky to me and I thought there might be a more conventional way. I'm new to ReactJS so if I need to provide more context, let me know!
MY CODE:
LEGEND.JSX
var React = require('react');
var cn = require('classnames');
// Have legend hold state about how many clicked
var Legend = React.createClass({
getInitialState: function () {
return { clicked: 0 }
},
render: function () {
console.log(this.props.children);
return (
<ul className="legend inline-list">
{this.props.children}
</ul>
);
},
});
Legend.Item = React.createClass({
getInitialState: function () {
return { hover: false, clicked: false };
},
handleMouseOver: function () {
this.setState({ hover: true });
this.props.mouseOver(this.props.name);
},
handleMouseOut: function () {
this.setState({ hover: false });
this.props.mouseOut(this.props.name);
},
handleClick: function() {
if (this.state.clicked) {
this.setState({ clicked: false });
this.props.click(this.props.name);
} else {
this.setState({ clicked: true });
this.props.click(this.props.name);
};
},
render: function () {
var swatchClasses = cn({ 'swatch': true, 'legend-item-fade': this.state.hover, 'c3-legend-item-hidden': this.state.clicked })
var spanClasses = cn({ 'legend-item-fade': this.state.hover, 'c3-legend-item-hidden': this.state.clicked })
return (
<li className="legend-item">
<i className={swatchClasses}
onClick={this.handleClick}
onMouseEnter={this.handleMouseOver}
onMouseLeave={this.handleMouseOut}
style={{ "backgroundColor": this.props.color }}></i>
<span className={spanClasses}
onClick={this.handleClick}
onMouseEnter={this.handleMouseOver}
onMouseLeave={this.handleMouseOut}>
{this.props.name}
</span>
</li>
);
},
});
module.exports = {
Legend: Legend,
};
RESPONSE.JSX RENDER FUNCTION
<Legend>
{newColumns.map(function (column) {
return (
<Legend.Item name={column.name}
color={column.color}
click={this.onLegendClick}
mouseOut={this.onLegendMouseOut}
mouseOver={this.onLegendMouseOver}/>
);
}.bind(this))}
</Legend>
I think the best and simplest way is to use callbacks.
In Legend recreate the components from the children, augmenting their props with a callback to Legend:
let legendItems = React.Children.map(this.props.children, child =>
React.cloneElement(child, { updateLegendCounter: this.updateLegend})
);
The callback in Legend is something like this:
updateLegend() {
this.setState({clicked: clicked + 1})
}
And finally, in your render method, you discriminate when
if (this.state.clicked === children.length-1)
Also, I would pass the initial state of clicked as a prop to the Item element. In this way it becomes really easy to select/deselect all.
I have a dynamic list of children, that are form inputs.
ex:
var FormRows = React.createClass({
getInitialState: function() {
return {
rows: []
}
},
createRows: function() {
this.props.values.maps(value){
rows.push(<FormRow ...handlers... ...props... value={value} />
}
},
addNewRow{
// add a new row
},
render: function() {
return (
<div>
{this.state.rows}
</div>
);
});
var FormRow = React.createClass({
getInitialState: function() {
return {
value: this.props.value || null
}
},
render: function() {
<input type='text' defaultValue={this.state.value} ...changeHandler ... }
}
});
This is a dumbed down version , but the idea, is a its a dynamic form, where the user can click a plus button to add a row, and a minus button, which will set the row to visibility to hidden.
This state is nested n levels deep. What is the best way to actually get the state out of the children, and submit the form? I can use 'ref' add a function to getFormValue(): { return this.state.value } to the FormRow button, but i'm not sure if thats the best practice way.
I find myself using this pattern quite often, an array of undetermined size of children, that need to pass the state up.
Thanks
It’s not a dumb question at all, and a good example of using flux principals in React. Consider something like this:
var App
// The "model"
var Model = {
values: ['foo', 'bar'],
trigger: function() {
App.forceUpdate()
console.log(this.values)
},
update: function(value, index) {
this.values[index] = value
this.trigger()
},
add: function() {
this.values.push('New Row')
this.trigger()
}
}
var FormRows = React.createClass({
addRow: function() {
Model.add()
},
submit: function() {
alert(Model.values);
},
render: function() {
var rows = Model.values.map(function(value, index) {
return <FormRow key={index} onChange={this.onChange} index={index} value={value} />
}, this)
return (
<div>{rows}<button onClick={this.addRow}>Add row</button><button onClick={this.submit}>Submit form</button></div>
)
}
})
var FormRow = React.createClass({
onChange: function(e) {
Model.update(e.target.value, this.props.index)
},
render: function() {
return <input type='text' defaultValue={this.props.value} onChange={this.onChange} />
}
});
App = React.render(<FormRows />, document.body)
I used a simplified model/event example using Array and forceUpdate but the point here is to let the model "own" the form data. The child components can then make API calls on that model and trigger a re-render of the entire App with the new data (Flux).
Then just use the model data on submit.
Demo: http://jsfiddle.net/ekr41bzr/
Bind values of inputs to some model (for example build in Backbone or Flux) and on submit retrieve values from there, without touching inputs.