How to force reactJS to redraw? - javascript

I am trying to update my table with a ajax call by using this.setState({ data: data }); but the datatable is not redrawn with the new data? (I can see that new data is received)
var GridRow = React.createClass({
render: function() {
var data = [], columns;
if(this.props.columns){
for(var i = 0; i < this.props.columns.length; i++){
data.push({
HTMLclass: this.props.columns[i].HTMLClass,
content: this.props.cells[i]
})
}
}
columns = data.map(function(col, i) {
return (
<td className={col.HTMLclass} key={i}>{col.content}</td>
);
}.bind(this));
return (
<tr>
{columns}
</tr>
);
}
});
var GridHead = React.createClass({
render: function() {
if(this.props.data){
var cell = this.props.data.Title;
var htmlClass = this.props.data.HTMLClass;
}
return (
<th className={htmlClass}>{cell}</th>
);
}
});
var GridList = React.createClass({
render: function() {
var tableClass = this.props.tableClass;
if(this.props.data){
var header = this.props.data.Columns.map(function (columns, i) {
return (
<GridHead data={columns} key={i} />
);
});
var row = this.props.data.Rows.map(function (row, i) {
return (
<GridRow columns={data1.Columns} cells={row.Cells} key={i} />
);
});
}
return (
<table className={tableClass}>
<tr>{header}</tr>
<tbody>
{row}
</tbody>
</table>
);
}
});
var GridPager = React.createClass({
handleClick: function(e) {
e.preventDefault();
this.props.onPaging();
},
render: function() {
return(
<a href='#' onClick={this.handleClick}>Paging</a>
);
}
});
var gridPage = 0;
var GridBox = React.createClass({
loadCommentsFromServer: function() {
var xhr = new XMLHttpRequest();
gridPage++;
xhr.open('get', this.props.url + '/?pageNr=' + gridPage, true);
xhr.onload = function() {
var data = JSON.parse(xhr.responseText);
this.setState({ data: data });
}.bind(this);
xhr.send();
},
handlePaginSubmit: function(comment) {
this.loadCommentsFromServer();
},
getInitialState: function() {
return { data: this.props.initialData };
},
render: function(){
var tableClass = this.props.tableClass;
return (
<div>
<GridList data={this.state.data} tableClass={tableClass} />
<GridPager onPaging={this.handlePaginSubmit} />
</div>
);
}
});

that = this
xhr.onload = function() {
var data = JSON.parse(xhr.responseText);
that.setState({ data: data });
}.bind(this);
In your code this is the context of the callback method where setState method is not available, but it's a callback and you don't get an error about it. Use the trick listed above so you have a reference to the right context.
setState() in react js ALWAYS causes rerender.

If your props are updating then better to use
componentWillReceiveProps(nextProps) {
this.props.data = nextProps.data;
}
react will render the component when state changes so you can use
this.setState(this.state)
If you want to update your render() method reads from something other than this.props or this.state, you'll need to tell React when it needs to re-run render() by calling
forceUpdate()

Related

ReactJs - getInitialState not working

I'm playing with ReactJs, and i have this simple code
var User = React.createClass({
render: function () {
return(
<li>
{this.props.email}
</li>
);
}
});
var UserList = React.createClass({
reload: function () {
var xhr = new XMLHttpRequest();
xhr.open('get', "http://localhost:64501/Home/GetUsers", true);
xhr.onload = function () {
var result = JSON.parse(xhr.responseText);
this.setState({ data: result });
console.log(JSON.stringify(result));
}.bind(this);
xhr.send();
},
getInitialState: function () {
return {
data: [
{ email: "bob#gmail.com", id: "1" },
{ email: "boby#gmail.com", id: "2" }
]
};
},
componentDidMount: function () {
window.setInterval(this.reload, 3000);
},
render: function () {
if (this.props.data != null) {
var userNodes = this.props.data.map(function (user) {
return (
<User email={user.email} key={user.id } ></User>
);
});
return (
<div>
<ul>{userNodes}</ul>
</div>
);
}
else {
console.log("this.props.data is null");
return null;
}
}
});
ReactDOM.render(<UserList />, document.getElementById('root'));
I got 2 issues :
1 - the datas returned by getInitialState function are not rendered by the component.
2 - setState does not refresh the component in the reload function.
You are reading this.props.data and you should be reading this.state.data.
Note the difference between component properties, which come externally, and component internal state.
Just replace all this.props.data with this.state.data and your code should work.

How get middle component's instance to pass its function to child in reactjs?

If I have a component:
var OuterComponent = React.createClass( {
render: function() {
return (
<div>
<MiddleComponent>
<Child parentFunctionShouldBe={this.middleComponentFunction} />
</MiddleComponent>
</div>
);
}
});
How do i pass the function that lives in the middle component? The issue is that this refers to the OuterComponent, not the MiddleComponent. I don't want it to be a static function, but an instance function.
Another option would be to use a Higher-order Component (HoC). This is a good choice if MiddleComponent is primarily adding markup around or capabilities to Child.
var MiddleComponent = function (Wrapped) {
var Component = React.createClass({
middleComponentFunction: function () {},
render: function () {
return (
<Wrapped {...this.props} parentFunctionShouldBe={this.middleComponentFunction} />
);
}
});
return Component;
}
var WrappedChild = MiddleComponent(Child);
var OuterComponent = React.createClass( {
render: function() {
return (
<div>
<WrappedChild passToChild="value" />
</div>
);
}
});
Further, you can customize how the integration occurs if you want to use MiddleComponent with other types of components.
var MiddleComponent = function (config, Wrapped) {
var prop = config.prop || 'parentFunctionShouldBe';
var Component = React.createClass({
middleComponentFunction: function () {},
render: function () {
var extraProps = {};
extraProps[prop] = this.middleComponentFunction;
return <Wrapped {...this.props} {...extraProps} />;
}
});
return Component;
}
// same result as above but configurable for other use cases
var WrappedChild = MiddleComponent({prop: 'parentFunctionShouldBe'}, Child);
var OuterComponent = React.createClass( {
render: function() {
return (
<div>
<WrappedChild passToChild="value" />
</div>
);
}
});
You can add a ref to a component, and call the method from the this.refs.componentRef:
var OuterComponent = React.createClass( {
callMethod: function() {
this.refs.middleComponent.middleComponentFunction();
},
render: function() {
return (
<div>
<MiddleComponent ref="middleComponent">
<Child parentFunctionShouldBe={this.callMethod} />
</MiddleComponent>
</div>
);
}
});
You can do something like this
var OuterComponent = React.createClass( {
render: function() {
return (
<div>
<MiddleComponent>
<Child />
</MiddleComponent>
</div>
);
}
});
var MiddleComponent = React.createClass({
propTypes: {
children: React.PropTypes.element.isRequired
},
render: function() {
return (
<div>
{React.cloneElement(this.props.children, {parentFunctionShouldBe: this.middleComponentFunction})}
</div>
)
},
middleComponentFunction: function() {
...
}
});

load json with reactjs

I'll be very grateful who can help me with this line
I've this:
var Country = React.createClass({
render:function(){
return(
<nav>
<h2>list of country:</h2>
{ this.props.country}
</nav>
)
}
})
var Jugador =React.createClass({
componentWillMount: function(){
var pais;
var self = this;
$.getJSON("https://restcountries.eu/rest/v1/all", function(data){
for(pais in data)
{
console.log(pais, data[pais].name);
return(
<Country key={i} country={self.render(data[pais].name)}> </Country>
)
}
})
},
})
and it does not work, and appear this error
Uncaught Invariant Violation: createClass(...): Class specification must implement a render method.
Your Jugador component needs to implement a render method.
In React each component must have render method, you should implement it. I've refactored your code and now it look like this
var Country = React.createClass({
render: function() {
return <div>
{ this.props.country }
</div>
}
})
var Countries = React.createClass({
getInitialState: function () {
return { countries: [] };
},
componentWillMount: function(){
$.getJSON("https://restcountries.eu/rest/v1/all", function (data) {
this.setState({ countries: data })
}.bind(this))
},
render: function () {
var countries = this.state.countries.map(function (el, i) {
return <Country key={i} country={ el.name } />
}, this);
return <nav>
<h2>list of country:</h2>
<div>{ countries }</div>
</nav>;
}
})
Example

repeating child with this.props of parent react element

Currently i m working on creating react components, and i need to repeat the child components, based on this.props.value of parent component.
I am struggling to find any good examples.
here is my code
var LiComponent = React.createClass({
render:function(){
return(
<div>
<span>{this.props.label}</span>
<span>{this.props.value}</span>
</div>
);
}
});
var StatsComponent = React.createClass({
getInitialState: function(){
return{
value: this.props.value || []
}
},
componentDidMount: function(){
var data = this.state.value,
columnName = data[0],
data = data.slice(1),
values = data.map(function (o) { return o.value; }),
labels = data.map(function (o) { return o.label; });
},
shouldComponentUpdate: function(nextProps, nextState){
var data = nextProps.value,
labels = data.map(function (o) { return o.label; }),
values = data.map(function (o) { return o.value; });
return false;
},
render: function(){
return (
<div style={style}>
<LiComponent>
</LiComponent>
</div>
);
}
});
now, i want to repeat the LiComponent according to the this.props.value of Stats Component. How should i do that?
You can push LiComponents to an array, and render these.
Like this,
render: function(){
var rows = this.props.value.map(function(row) {
//Add props to your LiComponent just as you would normally.
return <LiComponent />;
});
return (
<div style={style}>
{rows}
</div>
);
}

How to add keys the proper way to this react code

I get warnings about missing keys on GridRow in the following code. I have read about it but I do not find a way to apply it to the react component? It seems like I have to do it in the final render method but I don't have any data there?
var data1 = {"Columns":[{"Title":"Title1","HTMLClass":"g1_Title"},{"Title":"Title2","HTMLClass":"g2_Title"},{"Title":"Title3","HTMLClass":"g3_Title"}],"Rows":[{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]}]};
var GridRow = React.createClass({
render: function() {
var data = [], columns;
if(this.props.columns){
for(var i = 0; i < this.props.columns.length; i++){
data.push({
HTMLclass: this.props.columns[i].HTMLClass,
content: this.props.cells[i]
})
}
}
columns = data.map(function(col) {
return (
<div className={col.HTMLclass}>{col.content}</div>
);
}.bind(this));
return (
<li>
{columns}
</li>
);
}
});
var GridHead = React.createClass({
render: function() {
if(this.props.data){
var cell = this.props.data.Title;
var htmlClass = this.props.data.HTMLClass;
}
return (
<div className={htmlClass}>{cell}</div>
);
}
});
var GridList = React.createClass({
render: function() {
if(this.props.data){
var header = this.props.data.Columns.map(function (columns, i) {
return (
<GridHead data={columns} key={i} />
);
});
var row = this.props.data.Rows.map(function (row, i) {
return (
<GridRow columns={data1.Columns} cells={row.Cells} key={i} />
);
});
}
return (
<ul>
<li>{header}</li>
{row}
</ul>
);
}
});
var GridBox = React.createClass({
loadCommentsFromServer: function() {
var xhr = new XMLHttpRequest();
xhr.open('get', this.props.url, true);
xhr.onload = function() {
var data = JSON.parse(xhr.responseText);
this.setState({ data: data });
}.bind(this);
xhr.send();
},
getInitialState: function() {
return { data: this.props.initial };
},
render: function(){
return (
<GridList data={data1} />
);
}
});
Warning : Each child in an array should have a unique "key" prop.
Check the render method of GridRow. See
fb.me/react-warning-keys for more information.
Just add the key to your column div, React just needs some unique identifier so it can keep it's Virtual DOM diff up-to-date.
The key can be anything, just as long as it's unique.
columns = data.map(function(col, i) {
return (
<div className={col.HTMLclass}>{col.content} key={i}></div>
);
}.bind(this));
The key should only be set to the index if there are no other options.

Categories