How do you render div objects from non-React libraries in React? - javascript

I found a library I want to use in my project, but it's a plain JavaScript library and doesn't know anything about React.
https://www.npmjs.com/package/json-formatter-js
Is it possible to use this in my React project? I tried this, but the render crashes.
class JSONView extends Component {
constructor(props) {
super(props);
}
render() {
const formatter = new JSONFormatter(this.props.data);
const Rendered = formatter.render();
return (
<div>
<Rendered />
</div>
);
}
}
The error I get is this.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
The typeof Rendered is object.

Try this
class JSONView extends React.Component<any, any> {
constructor(props) {
super(props);
}
public refs:{
JsonDiv: HTMLDivElement;
};
componentDidMount(){
const formatter = new JSONFormatter(this.props.data);
this.refs.JsonDiv.appendChild(formatter.render());
}
render() {
return (
<div ref='JsonDiv'>
</div>
);
}
}
or this one
class JSONView extends React.Component<any, any> {
constructor(props) {
super(props);
}
render() {
const formatter = new JSONFormatter(this.props.data);
return (
<div dangerouslySetInnerHTML={formatter.render()}>
</div>
);
}
}

Looks like the library returns HTMLDOMElement from function formatter.render().
So you can't use JSX syntax to render it.
While I agree that the other answers can work, I would prefer using a dom ref and appending the returned HTMLDOMElement from formatter.render() in componentDidMount lifecycle method.
import React, { Component } from 'react';
import { render } from 'react-dom';
import JSONFormatter from "json-formatter-js";
import Hello from './Hello';
import './style.css';
class JSONView extends Component {
constructor(props) {
super(props);
}
ref = null;
componentDidMount(){
const formatter = new JSONFormatter(this.props.data);
const Rendered = formatter.render();
this.ref.appendChild(Rendered);
}
render() {
return (
<div ref={e => this.ref = e}>
</div>
);
}
}
render(<JSONView data={{Hello: {Hello: "World"}}} />, document.getElementById('root'));
Working Demo You can drill down to the object.
In the other approach which makes use of dangerouslySetInnerHTML or just rendering the string content, you have the risk of losing DOM events.
import React, { Component } from 'react';
import { render } from 'react-dom';
import JSONFormatter from "json-formatter-js";
import Hello from './Hello';
class JSONView extends Component {
constructor(props) {
super(props);
}
ref = null;
render() {
const formatter = new JSONFormatter(this.props.data);
console.log(formatter);
const Rendered = formatter.render();
console.log(Rendered.innerHTML);
return (
<div>
<div dangerouslySetInnerHTML={{__html:Rendered.innerHTML}} />
</div>
);
}
}
render(<JSONView data={{Hello: {Hello: "World"}}} />, document.getElementById('root'));
Working Link You cannot drill down to the object.

edit
try
{ Rendered }
there might be warning if it ships class with html tags.
between curly braces {} and it'll work (99 %) and if it doesn't then continue reading.
you can render if it returns string.
currently it might b returning html object which is incompatible with react dom render.
if it contains class property then it'll cause problem even if it's in string format.
you can use react-html-parser to render html strings in react. reference
const Rendered = formatter.render().outerHTML // this will give you html string instead of html object
now use react-html-parser if you don't wanna manage class attribute name conflict as react accepts className
import ReactHtmlParser from 'react-html-parser';
class JSONView extends Component {
constructor(props) {
super(props);
}
render() {
const formatter = new JSONFormatter(this.props.data);
const Rendered = formatter.render().outerHTML;
return (
<div>
{ ReactHtmlParser(Rendered) }
</div>
);
}
}

Related

How can I append an item to a list in js in reactjs

I created a Parent Component js file and changed the state fo numbers list by adding 10 to it but it is showing error as there is no push funtion.Could anyone help me with this??
import React, { Component,PureComponent } from 'react'
import Reg from './Regularcomp'
import Pure from './PureComp'
export class Parentcomp extends PureComponent {
constructor(props) {
super(props)
this.state = {
name:"Mayank",
numbers:[1,2,3,4,5,6]
}
}
componentDidMount()
{
setInterval(()=>{
this.state.numbers = this.state.numbers.push(10)
},1000)
}
render() {
console.log("*****Parent Component*******")
return (
<div>
Parent is Pure Component
<Reg name={this.state.name}/>
<Pure name={this.state.name}/>
</div>
)
}
}
export default Parentcomp
state interface in react class components is a complex structure. It doesn't provide you any plain setters on your data structrue. The only setter you may use on it is setState method. So you should rewrite your componentDidMount as:
componentDidMount() {
setInterval(() => {
this.setState((prevState) => ({
numbers: [...prevState.numbers, 10]
}));
}, 1000);
}

Variable not defined (TypeError: Cannot read property 'todos' of null) ReactJS

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;

React Higher Order Components to add a custom attribute to the rendered JSX

I'm trying to write a React Higher Order Components to add a custom attribute "test_id" to the view of a wrappedComonent, I need that auto-genrated attribute to do some UI testing later. but I have not find a way to achieve that.
import React, {Component, PropTypes} from "react";
const wrapTestableComponent = (ComponentToWrap) => {
class TestableComponent extends Component {
constructor(props){
super(props);
}
render() {
return <ComponentToWrap {...this.props} test_id={this.props.test_id} />;
}
}
TestableComponent.propTypes = {
test_id: PropTypes.string.isRequired,
}
return TestableComponent
}
export default wrapTestableComponent;
I've also tried the below version but I got that error: Uncaught TypeError: Can't add property test_id, object is not extensible
import React, {Component, PropTypes} from "react";
const wrapTestableComponent = (ComponentToWrap) => {
class TestableComponent extends Component {
constructor(props){
super(props);
}
render() {
var wrappedComponentView = <ComponentToWrap {...this.props} />;
wrappedComponentView.test_id = this.props.test_id;
return <ComponentToWrap {...this.props} />;
}
}
TestableComponent.propTypes = {
test_id: PropTypes.string.isRequired,
}
return TestableComponent
}
export default wrapTestableComponent;
EDIT
According to the comments we discussed below, I misunderstood the question before and I revised my answer.
The way you use in http://pastebin.com/0N9kKF73 should be the best way to do what you want.
I've tried to make a function that returns React.creatElement() to make a copy and assigned the extra props for ComponentToWrap but failed because of two main reasons.
React.creatElement() needs a param type.
ReactJS supported HTML attributes
REF:
Get HTML tag name from React element?
React: Can I add attributes to children's resultant HTML?
The revised version from your pastebin and the internet.
const wrapTestableComponent = (ComponentToWrap) => {
class TestableComponent extends React.Component {
componentDidMount() {
ReactDOM.findDOMNode(this.wrappedRef).setAttribute('test_id', this.props.test_id);
}
render() {
return <ComponentToWrap {...this.props}
ref={() => { this.wrappedRef = this; }}
/>;
}
}
TestableComponent.propTypes = {
test_id: React.PropTypes.string.isRequired,
}
return TestableComponent
}
const TestComp = (props) => (<div>Here is the TestComp</div>)
const NewComponent = wrapTestableComponent(TestComp)
ReactDOM.render(<NewComponent test_id="555" />, document.getElementById('View'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="View"></div>
I suppose this is what are you trying to find. ref may do all magic for you. Actually i dont have any others idea how to add custom component. fiddle
export function SelectWrapper(Select){
return class Wrapper extends Component {
componentDidMount(){
var element = ReactDOM.findDOMNode(this.refs.test);
element.setAttribute('custom-attribute', 'some value');
}
...
render(){
return <Select {...this.props} ref='test'/>
}
}
}

Is there a React lifecycle method to do something only when component receive props the first time?

I'm new to React so thank you for your patience in advance. Also using Redux.
I have a list of content pulled from the API, I display the text and a hidden text box and on a state change associated that alternates the visibility of the two. Essentially user can click on the text and edit the text, achieved by inverting the boolean and swapping the display. They can then save it and PUT to server etc.
Since my list length varies, I must initialize a number of state.isVisible[n]. equivalent to the number of content being displayed each time. This number must be counted, after the props come in. I am using Redux so the content is retrieved, stored, then given to props. It's done as the following:
constructor(props){
super(props);
this.state = {
isVisibleObj: {}
}
}
componentWillReceiveProps(){
const { isVisibleObj } = this.state
// set visibility of text box
let obj = {}
Object.keys(this.props.questions).forEach(key => obj[key] = false)
this.setState({isVisibleObj: obj})
}
My initial implementation was that in componentWillReceiveProps I do all the setState() to initialize the isVisible properties to a boolean.
The challenge I am having with this implementation is that, if a user open up multiple items for edit, and if she saves one of them, the PUT request on success would send back the edited content, now updating the store and props. This will trigger componentWillReceiveProps and reset all the visibilities, effectively closing all the other edits that are open.
Any suggestion on how to proceed?
I think you should make two components
List (NamesList.react)
import React, {PropTypes} from 'react';
import NameForm from './NameForm.react';
import Faker from 'Faker'
export default class NamesList extends React.Component {
constructor(){
super();
this.addItem = this.addItem.bind(this);
}
addItem(){
var randomName = Faker.name.findName();
this.props.addName(randomName);
}
render() {
let forms = this.props.names.map((name,i) => {
return <NameForm updateName={this.props.updateName} index={i} key={i} name={name} />
});
return (<div>
<div>{forms}</div>
<button onClick={this.addItem}>Add</button>
</div>);
}
}
NamesList.propTypes = {
names: PropTypes.arrayOf(PropTypes.string).isRequired
};
Form (NameForm.react)
import React, {PropTypes} from 'react';
export default class NameForm extends React.Component {
constructor(props) {
super(props);
this.updateName = this.updateName.bind(this);
this.state = {
showTextBox:false
}
}
updateName(){
this.setState({showTextBox:false});
this.props.updateName(this.props.index,this.refs.name.value);
}
render() {
if(this.state.showTextBox){
return (<div>
<input ref="name" defaultValue={this.props.name} />
<button onClick={this.updateName}>Save</button>
</div>);
}
return (<div onClick={() => {this.setState({showTextBox: !this.state.showTextBox})}}>
{this.props.name}
</div>);
}
}
NameForm.propTypes = {
name:PropTypes.string.isRequired
};
Invoke (App.js)
import React, { Component } from 'react';
import NamesList from './NamesList.react';
class App extends Component {
constructor(){
super();
this.addName = this.addName.bind(this);
this.updateName = this.updateName.bind(this);
this.state = {
names:['Praveen','Vartika']
}
}
addName(name){
let names = this.state.names.concat(name);
this.setState({
names: names
});
}
updateName(index,newName){
let names = this.state.names.map((name,i) => {
if(i==index){
return newName
}
return name;
});
this.setState({names:names});
}
render() {
return (
<NamesList names={this.state.names} updateName={this.updateName} addName={this.addName} />
);
}
}
export default App;
Now if your store changes after user saves something. React wont re-render Child component that didn't change

how to create a class and another classes extends that class in react js

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

Categories

Resources