I am trying to incorporate 'canvas-datagrid' module into React. However, I keep on getting this error:
Uncaught Error: Objects are not valid as a React child (found: [object HTMLElement]). If you meant to render a collection of children, use an array instead. ... in grid (created by CanvasGrid)...
The code is a slight modified version of the one on the React example:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import CanvasDataGrid from 'canvas-datagrid';
class CanvasGrid extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const args = {};
this.grid = ReactDOM.findDOMNode(this);
this.updateAttributes();
}
componentWillReceiveProps(nextProps) {
this.updateAttributes(nextProps);
}
shouldComponentUpdate() {
return false;
}
componentWillUnmount() {
this.grid.dispose();
}
updateAttributes(nextProps) {
Object.keys(this.props).forEach(key => {
if (!nextProps || this.props[key] !== nextProps[key]) {
if (this.grid.attributes[key] !== undefined) {
this.grid.attributes[key] = nextProps ? nextProps[key] : this.props[key];
} else {
this.grid[key] = nextProps ? nextProps[key] : this.props[key];
}
}
});
}
render() {
return (
<div>
<CanvasDataGrid />;
</div>
);
}
}
export default CanvasGrid;
As per my understanding of the example, there isn't anything special to be done, however, the error above is encountered when React tries to render <CanvasDataGrid> component.
The npm package canvas-datagrid exports a Web Component, not a react component. You have to render that component in your UI using react.
What you have to do is include the script in your index.html and then create a React component CanvasGrid with a render function:
render() {
return React.createElement('canvas-datagrid', {});
}
For full component code, see this file.
Related
I have 2 components in my react application. On first time page load, the first component is supposed to make a query and display data(buttons) accordingly. The state of second component till now is empty. When the user clicks on any of the button, another request should be made to the sever and state of the second component should be changed and should be reflected on the web page.
These are my files..
Apps.js
import React, { Component } from 'react';
import './App.css';
import OrgList from "./orgList"
import OrgDetails from "./orgDetails"
class App extends Component {
render() {
return [
<OrgList/>,
<OrgDetails/>
];
}
}
export default App;
orgList.js
import React, { Component } from 'react'
import OrgDetails from "./orgDetails"
var posts =[]
class OrgList extends Component {
constructor(props){
super(props);
this.state={
mainpost: [],
devices:[],
}
}
componentDidMount(){
fetch(someURL)
.then(res => res.json())
.then(function (data){
for (let i = 0; i < 3; i++){
posts.push(data.orgs[i].name)
}
}).then(mainpost => this.setState({mainpost:posts}));
}
render() {
var token =new OrgDetails();
const postItems =this.state.mainpost.map((post) => (
console.log(post),
<button
data-tech={post}
key={post}
className="org-btn"
onClick={() => token.dispatchBtnAction(post)}
>
<h3>{post}</h3>
</button>
)
)
return (
<div>
<h3> Organisations!!!! </h3>
<h5>{postItems}</h5>
</div>
)
}
}
export default OrgList;
orgDetails.js
import React, { Component } from 'react'
var list =[]
const orgname = org =>
`someURL/${org}`
class OrgDetails extends Component {
state={
devices:[],
}
constructor(props){
super(props);
this.state={
devices: [],
}
this.dispatchBtnAction=this.dispatchBtnAction.bind(this)
}
dispatchBtnAction=(str) => {
list =[]
fetch(orgname(str))
.then(res => res.json())
.then(function (data){
for (let i = 0; i < 3; i++){
//console.log("123")
list.push(data.devices[i].location)
console.log(list)
}
}).then(devices => this.setState({
devices : list,
}));
}
render() {
const devices=this.state.devices.map((dev,i)=>(
<div key={dev}>
<li>{dev}</li>
</div>
))
return (
<div>
<p>{devices}</p>
</div>
)
}
}
export default OrgDetails;
But I am getting this warning...
Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the OrgDetails component.
Because of this, the state is not getting changed and the component is not rerendering.
How to eliminate this warning and if any better method is there please do suggest.
As these 2 component are not parent-child components, perhaps you should implement all the logic in the App and than pass state-handlers as props to each component.
Then your components will look something like this:
class App extends Component {
state = { clicks: 0 }
incrementState = () {
const prev = this.state.clicks;
this.setState({ clicks: prev + 1 })
}
render() {
return [
<DisplayComponent counter={this.state.clicks} />,
<ControlComponent onIncrement={this.incrementState} />
];
}
}
Component that displays state
class DisplayComponent extends Component{
render() {
return (<h3>this.props.counter</h3>);
}
}
Component that handles state
class ControlComponent extends Component {
render() {
return (<button onClick={this.props.onIncrement}>click me</button>)
}
}
Well the whole issue is this line var token =new OrgDetails(); This just creates the object. But doesn't mount it in the DOM. It also doesn't reference to the component <OrgDetails/> created in App. So when you try to use token.dispatchBtnAction(post), you are trying to setState on a component that is not mounted in the DOM, hence the error.
This is a really questionable way of making communication in between two components. You are better off using a Parent-Child relationship in between component. Also you can have a look at making Presentational Component and Container components differentiation to make the workflow easy. Have a read at the this link.
This is all my code below .
When I run it I receive this error (TypeError: Cannot read property 'todos' of null )todos not found at this line var todos=this.state.todos;
My App.js file
import React, { Component } from 'react';
class App extends Component {
getInitialState (){
return{
todos:['washup',"hi","hello","up"]
}
}
render() {
var todos=this.state.todos;
Added Code here
todos=todos.map(function(item,index){
return(
<li>item</li>
);
}
);
Till here
return (
<div id="App">
<ul>{todos}</ul>
)
} )
</div>
);
}
}
export default App;
This is my index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from `'./registerServiceWorker';`
ReactDOM.render(<div>
<App>Here is my Buttonas</App>
</div>, document.getElementById('root'));
registerServiceWorker();
EDIT
New Error
TypeError: Cannot read property 'map' of undefined
At this line todos=todos.map(function(item,index){
What is the error now?
getInitialState() is only used with createReactClass(). When using ES6 classes you just set state as a property:
See Setting the Initial State in the react docs:
In ES6 classes, you can define the initial state by assigning
this.state in the constructor:
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: ['washup',"hi","hello","up"],
}
}
// ...
}
or just
class App extends Component {
state = {
todos: ['washup',"hi","hello","up"],
}
// ...
}
With createReactClass(), you have to provide a separate
getInitialState method that returns the initial state:
var App = createReactClass({
getInitialState: function() {
return {
todos: ['washup',"hi","hello","up"],
};
},
// ...
});
You're initializing the state older way in a newer version of reactjs. I already appreciate the answer of trixn. But here's also a solution without removing your current code:
class App extends Component {
state = getInitialState (){
return{
todos:['washup',"hi","hello","up"]
}
}
Notice that I have assigned state to the getInitialState and will work fine because this returns the object {todos:['washup',"hi","hello","up"]} which is similar to this:
state = {todos:['washup',"hi","hello","up"]}
Next, when your component is being rendered first time your todos might get undefined as you stated. To resolve this issue you may add a condition:
todos && todos.length && todos.map(...)
Now, the map function will only run if the todos is not undefined and it has length ie. it has at least one value.
It caused because you didn't define todos in your state, to achieve the soloution, make a constructor in your class and set a todos variable in your state, you can set in empty or null in the constructor and fill it later, then you can use it in your render section, comment if you need further information and also read react life cycle in the official website
With createClass you can use getInitialState:
const App = React.createClass({
getInitialState() {
return { /* initial state */ };
},
});
but with ES6 classes you do like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {todos:['washup',"hi","hello","up"]};
}
}
EDITED: get items through map:
class App extends Component {
state={
todos: ['washup', "hi", "hello", "up"]
}
render() {
var todos= this.state.todos.map((item)=>{
return <li>{item}</li>
})
return (
<div id="App">
<ul>{todos}</ul>
</div>
)
}
}
Try this. You should define you todos in the state
App.Js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.state ={
todos:['washup',"hi","hello","up"]
}
}
render() {
return (
<div id="App">
<ul>{this.state.todos}</ul>
</div>
);
}
}
export default App;
I am using the react-chat-widget and trying to call a function in the base class of my application from a custom component rendered by the renderCustomComponent function of the widget.
Here is the code for the base class:
import React, { Component } from 'react';
import { Widget, handleNewUserMessage, addResponseMessage, addUserMessage, renderCustomComponent } from 'react-chat-widget';
import 'react-chat-widget/lib/styles.css';
import Reply from './Reply.js';
class App extends Component {
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, this.correct);
}
correct = () => {
console.log("success");
}
render() {
return (
<div className="App">
<Background />
<Widget
handleNewUserMessage={this.handleNewUserMessage}
/>
</div>
);
}
}
export default App;
And here is the code for the custom component Reply:
import React, { Component } from 'react';
import { Widget, addResponseMessage, renderCustomComponent, addUserMessage } from 'react-chat-widget';
class Reply extends Component {
constructor(props) {
super(props);
}
sendQuickReply = (reply) => {
console.log(this.props); //returns empty object
//this.props.correct(); <-- should be called
};
render() {
return (
<div className="message">
<div key="x" className={"response"}onClick={this.sendQuickReply.bind(this, "xx")}>xx</div>
</div>)
}
}
export default Reply;
According to ReactJS call parent method this should work. However, when I print the this.props object it is empty, although the documentation of the renderCustomComponent method states that the second argument of the component to render are the props that the component needs (in this case the parent class function).
Where have I gone wrong?
The second parameter is considered as props, but it is expected to be an object. you would pass it like
handleNewUserMessage = (newMessage) => {
renderCustomComponent(Reply, {correct: this.correct});
}
following the Flux pattern I'm trying to update my component and pass some values (a string and a boolean in this specific case) via the store.
I could not find any non-hacky way to solve this yet i.e. using global vars in the Store and use a getter function in the Store which is called from the component on ComponentWillMount(), not a nice solution.
Here's a stripped down code example to show what im trying to achieve:
ExampleStore.js
import AppDispatcher from '../appDispatcher.jsx';
var displayimportError = false;
var importedID = '';
import axios from 'axios';
class ExampleStore extends EventEmitter {
constructor() {
super();
}
importId(id) {
let self = this;
// fetch data from BE
axios.get('foo.com').then(function(response) {
if (response.data && response.data.favoriteEntries) {
displayimportError = false;
}
self.emitChange();
}).catch(function(error) {
console.log(error);
displayimportError = true;
importedID = id;
self.emitChange();
// now update component and pass displayimportError and
// importedID.
// best would to component.receiveUpdateFromStore(param); but
// it's giving receiveUpdateFromStore is not function back
});
}
}
var favObj = new ExampleStore();
AppDispatcher.register(function(payload) {
var action = payload.action;
switch (action.actionType) {
case 'UPDATE_ID':
favObj.importId(action.data);
break;
}
return true;
});
export default favObj;
As mentioned in the Comment above the best solution in my eyes so far would be to call a function in the component from the store i.e component.receiveUpdateFromStore(param); and then update the component state within that function but even though they seem to be im/exported correctly to me it is returning receiveUpdateFromStore is undefined.
Any other idea how to solve this is appreciated.
//example component
import React from 'react';
import ReactDom from 'react-dom';
import ExampleStore from '../stores/ExampleStore.jsx';
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
}
receiveUpdateFromStore(param) {
this.setState({'exampleText': param.text, 'exampleBoolean': param.bool});
}
render() {
return <div className="foo">bar</div;
}
}
export default ExampleComponent;
Any idea how to pass data from store to a component and update component state in a nice way?
I would hang your store state on the store class instance itself -- something like this.state.displayimportError = true -- and then have the component subscribe to the store:
import React from 'react';
import ExampleStore from '../stores/ExampleStore.jsx';
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
importError: ExampleStore.state.displayimportError,
};
}
componentWillMount() {
ExampleStore.on( 'change', this.updateState );
}
componentWillUnmount() {
ExampleStore.removeListener( 'change', this.updateState );
}
updateState = () => {
this.setState( state => ({
importError: ExampleStore.state.displayimportError,
})
}
render() {
return <div>{ this.state.importError }</div>
}
}
NOTE: Above code untested, and also using class properties/methods for binding updateState.
I am a beginner in react js, before react I was working with angular2 and backbone,and now my problem is I want to create a class such that all of my requests send from this class,like this:
class Ext {
get(url){
$.ajax({
url : url,
success : function(res){},
and ......
});
}
}
in my another component that use from my Ext function :
export default Ext;
import React from 'react';
import {render} from 'react-dom';
import {Ext} from "./module/Ext"
class App extends React.Component {
constructor(props) {
super(props);
/// Ext.get();
}
render () {
return(
<p> Hello React!</p>
);
}
}
render(<App/>, document.getElementById('app'));
how to extends from Ext ??? what is the best way ?
If your get(url) method is something general, it would be wise to have it as part of a separate module, then import and use it in any component you would like.
If, on the other hand, you want to implement a functionality right into a react component, the new ES2015 way of doing it would be by using Composition.
You first create what's called a HOC (Higher order component), which basically is just a function that takes an existing component and returns another component that wraps it. It encapsulates your component and gives it functionality you want, like with mixins but by using composition instead.
So your example would look like something like this:
import React from 'react';
export default const Ext = (Component) => class extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
let result = this.get('some_url').bind(this)
this.setState({ result })
}
get(url) {
$.ajax({
url : url,
success : function(res){
return res;
}
});
}
render() {
// pass new properties to wrapped component
return <Component {...this.props} {...this.state} />
}
};
Then you can just create a stateless functional component and wrap it with the HOC:
import React from 'react';
import Ext from './module/Ext';
class App {
render () {
return <p>{this.result}</p>;
}
}
export default Ext(App); // Enhanced Component
Or using ES7 decorator syntax:
import { Component } from 'react';
import Ext from './module/Ext';
#Ext
export default class App extends Component {
render () {
return <p>{this.result}</p>;
}
}
You can read this post for more details: http://egorsmirnov.me/2015/09/30/react-and-es6-part4.html