How to pass a javascript-generated DOM element to react render function - reactjs

I am a newbie to react. I'm trying to create a component named StationBrowserForHome (TSX). In the render() function, I want to render a DOM element looks like this:
<div class = "big-group">
<div class = "small-group>
<div class="item"></item>
<div class="item"></item>
...
<div class="item"></item>
</div>
<div class = "small-group>
<div class="item"></item>
<div class="item"></item>
...
<div class="item"></item>
</div>
...
</div>
So I tried to create that DOM tree in componentWillMount(), set it to state variable "divItems", and in render(), I retrieve it
export class StationBrowserForHome extends Component <{}, any> {
componentWillMount (){
var divItems = document.createElement("div");
var divSeven = document.createElement("div");
for (var i = 0; i < 7; i++) {
var singleItem = document.createElement('<div className="station-item"><div className="row"><StationBrowserItemInfo/></div><div className="row station-name">Station {i}</div></div>');
divSeven.appendChild(singleItem);
console.log("singleItem: "+singleItem);
console.log("divSeven: "+divSeven);
}
divItems.appendChild(divSeven);
this.setState({divItems:divItems});
}
render() {
return (
<div className="container-fluid text-center browser">
<div className="cover-div">
<ArrowButton/>
<div className="text-center list-station" id="station-browser">
{ this.state.divItems }
</div>
</div>
</div>
);
}
}
And I got this error message:
VM19339 invariant.js:42 Uncaught Error: Objects are not valid as a
React child (found: [object HTMLDivElement]). If you meant to render a
collection of children, use an array instead
Any help would be appreciated!

React is great because you can break things down into reusable components. This code kind of goes against the whole meanings of react.
Everything in componentWillMount should be inside a component with props numberOfItems for example and then based on that you will render what it seems to me would be a station item.
export class StationList extends React.Component<{stations: IStation[]}, {}> {
render() {
return (
<div className='stations'>
{this.getStationItems}
</div>
);
}
private getStationItems() {
const itemsDiv = this.props.stations.map((station, index) => {
return (
<div className='station'>
//........
// other stuff here
</div>
)
});
return itemsDiv;
}
}
Then back in the StationBrowserForHome component we just:
const stations: IStation[] = [
{
id: 'station1',
purpose: 'purpose1'
},
{
id: 'station2',
purpose: 'purpose2'
}
];
return (
<div className="container-fluid text-center browser">
<div className="cover-div">
<ArrowButton/>
<div className="text-center list-station" id="station-browser">
<StationList stations={stations} />
</div>
</div>
</div>
);
The code above is just a generic template not in front of an IDE but thats the basic gist.

why you use this way , you can easily create an element with JSX like this
componentWillMount() {
let ele = <div> </div>;
}
and if you want loop with element or any work on that you can use map function (method for looping in javascript like foreach) like this
componentWillMount() {
this.state.count.map( (e) => ( ... ) )
}
in above example e parameter , refer on your object for example
constructor(props) {
super(props);
this.state = {
data:[
{
id:1,
name:'hamid'
},
{
id:2,
name:'mat'
},
{
id:3,
name:'sara'
}
]
}
this.state.data.map( (item) => (
<li key={item.id} > {item.name} </li>
));
you have this in your DOM
<li>hamid</li>
<li>mat</li>
<li>sara</li>
and you must set key attribute in top level root element inside map function , and this must be uniq for each item

Its better to create on functional component and passed the required data for its rendering.
const MyComp = ({counter}) => {
return (
<div>{(" ".repeat(7).split("")).map((value, index) => (
<div class="big-group">
<div class="small-group">{index+1}</div> //for example
</div>
))
}</div>
);
}
Inside the render, passed the state data to it.data
render(){
return(<div>
....
<MyComp counter = {7} />
</div>);
}

Related

Accesing object using props in ReactJs

I'm trying to access object keys using props as an index but it's not working. Error: Objects are not valid as a React child (found: object with keys {id, name, img_url, location}). If you meant to render a collection of children, use an array instead.
I am new to React so I appreciate any help.
My code:
class ExpandCard extends React.Component {
render() {
const props = this.props;
const profiles = props.profiles;
return(
<>
<div className="">
{profiles[props.active]}
</div>
</>
);
}
}
class App extends React.Component {
state = {
profiles: testData,
active: null,
}
getActive = (dataFromCard) => {
console.log('the magic number is', dataFromCard);
this.setState({active: dataFromCard});
}
render() {
return (
<div>
<div className="wrapper">
<header>
<div className="logo">LOGO</div>
</header>
<Form />
<div className="cards">
<div className="card-list">
{this.state.profiles.map(profile => <Card key={profile.id} {...profile} activeCard={this.getActive} />)}
</div>
<div className="expand-card">
<ExpandCard active={this.state.active} profiles={this.state.profiles} />
</div>
</div>
</div>
</div>
);
}
}
It looks like {profiles[props.active]} returns an object that looks like this:
{ id, name, img_url, location }
You can't return an object from a React component, maybe you meant to return {profiles[props.active].name}?

Why is my component not getting re-rendered upon using setState and changing the value of state?

Why is it that the component Eachcartitem is not getting re rendered although I change the state. I have this fucntion which gets called from inside the Eachcartitem component:-
cartremover(a){
var cart1=cart;
var firstpart=cart.slice(0,a);
var secondpart=cart.slice(a+1,cart.length);
var final = firstpart.concat(secondpart);
this.setState({
cartstate:final,
abc:true
})
}
The Eachcartitem is used as follows in parent component:-
<div style={{float:'left',width:'65%'}}>
{
this.state.cartstate.map((cat) => (<Eachcartitem data={cat} index={this.state.cartstate.indexOf(cat)} cartremover={i=>this.cartremover()}/>))
}
<br></br><br></br>
</div>
And the Eachcartitem is as follows:-
class Eachcartitem extends React.Component{
constructor(props){
super(props);
this.state={
data:this.props.data
};
}
clicker(){
this.props.cartremover(this.props.index);
}
render(){
return(
<div className='cartdiv'>
<div style={{width:'100%',display:'inline'}}>
<h3 style={{width:'70%',float:'left',paddingLeft:'10px'}}>{this.state.data.productName}</h3>
<div style={{float:'right'}}>Rs.{this.state.data.productPrice}</div>
<div style={{width:'30%',float:'left',paddingLeft:'10px'}}>Store:{this.state.data.shopName}</div>
<div style={{width:'30%',float:'left',paddingLeft:'10px'}}>Quantity:{this.state.data.productQuantity}</div>
<br></br><br></br><br></br><br></br><br></br>
<div style={{width:'auto',float:'left',paddingLeft:'10px'}}>Variant:{this.state.data.variant.quantity}</div>
<br></br><br></br><br></br><br></br>
<div style={{width:'auto',float:'right',marginRight:'7px'}} onClick={()=>this.clicker()}>❌</div>
</div>
</div>
);
}
}
export default Eachcartitem
But for some reason the cartitem divs are not getting changed why is it so?
Its because you are not passing the index of the item to the function, here:
cartremover={i => this.cartremover()}
Write it like this:
cartremover={i => this.cartremover(i)}
You don't need to pass the index to child component, use this code:
this.state.cartstate.map((cat, i) => (
<Eachcartitem data={cat} cartremover={e => this.cartremover(i)} />
))
Now from Eachcartitem simply call that method: this.props.cartremover().
Better to use splice to remove the element at particular index, write the method like this:
cartremover(a){
let cart = [...this.state.cartstate];
cart.splice(a, 1);
this.setState({
cartstate: cart,
abc: true
})
}
Check working snippet:
const Temp = (props) => <div>
{props.name}
<button onClick={props.cartremover}>Delete</button>
</div>
class App extends React.Component {
constructor() {
super()
this.state = {cart: ['a', 'b', 'c', 'd', 'e']}
}
cartremover(a){
let cart = [...this.state.cart];
cart.splice(a, 1);
this.setState({
cart,
})
}
render() {
return (
<div>
{this.state.cart.map((cart, i) => (
<Temp key={cart} name={cart} cartremover={e => this.cartremover(i)}/>
))}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<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='app' />

Unable to delete list item in react. Error shows: Uncaught TypeError: Cannot read property 'remove' of undefined

I'm trying to make simple todo app.In this i want delete item from list by onClick function. The button onClick returns function named remove(i) which deletes the item in list. But I'm getting error as mentioned above.
The code is as follows:
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(){
super();
this.state={
todo:[]
};
};
entertodo(keypress){
var todo=this.refs.newtodo.value;
if( keypress.charCode == 13 )
{
this.setState({
todo: this.state.todo.concat(todo)
});
this.refs.newtodo.value=null;
};
};
todo(data,i){
return (
<li key={data.id} index={i}>
<input type="checkbox"className="option-input checkbox"/>
<div className="item">
{data}
<button onClick={this.remove.bind(this)}className="destroy"></button>
</div>
</li>
);
};
remove(i){
var todo=this.refs.newtodo.value;
var deletetodo=this.state.todo.concat(todo)
deletetodo.splice(i,1);
this.setState({todo:deletetodo})
};
render() {
return (
<div>
<div className="lines"></div>
<div>
<input type="text" ref= "newtodo" onKeyPress={this.entertodo.bind(this)}className="inputext"placeholder='todos'/>
</div>
<div className="app">
<ul>
{this.state.todo.map(this.todo.bind(this))}
</ul>
</div>
</div>
);
}
}
export default App;
The same problem arises if i try to call a function for to strikeoff the list item upon click on checkbox. Need help.
You need to bind your todo and remove function and also need to splice your todo array correctly. As your ref new is not defined and unique, you cannot access the value so just splice the array by index
class App extends React.Component {
constructor(){
super();
this.state={
todo:[]
};
};
entertodo(keypress){
var todo=this.refs.newtodo.value;
if( keypress.charCode == 13 )
{
this.setState({
todo: this.state.todo.concat(todo)
});
this.refs.newtodo.value=null;
};
};
todo = (data,i) => {
return (
<li>
<input type="checkbox"className="option-input checkbox"/>
<div key={data.id} className="item">
{data}
<button onClick={this.remove.bind(this, i)}className="destroy">Delete</button>
</div>
</li>
);
};
remove = (i) =>{
var deletetodo = {...this.state.todo};
this.state.todo.splice(i,1);
this.setState({todo:this.state.todo})
};
render() {
return (
<div>
<div className="lines"></div>
<div>
<input type="text" ref= "newtodo" onKeyPress={this.entertodo.bind(this)}className="inputext"placeholder='todos'/>
</div>
<div className="app">
<ul>
{this.state.todo.map(this.todo)}
</ul>
</div>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('app'));
<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="app"></div>

ReactJs event passed from Container to Presentational Components

Trying to wrap my head around passing events from Container to Presentational components.
As I understand it I attach the event to the Presentational/view level component. But I pass the functionality from the Container component to the Presentational component as a prop. I'm making sure to bind my function to this, but am getting a "Cannot read property 'onMouseEnterHandler' of undefined" error.
How am I not properly passing or binding this function?
class FeaturesContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
hovered: false
}
this.onMouseEnterHandler = this.onMouseEnterHandler.bind(this);
}
onMouseEnterHandler() {
this.setState({
hovered: true
})
console.log('mouse enter, ' + this.state.hovered);
}
render() {
return(
<div className="page features" >
<ul className="features-list">
{data.features.map(function(obj, i) {
return (
<li key={i}>
<Feature {...obj} onMouseEnterHandler={this.onMouseEnterHandler} />
</li>
)
})}
</ul>
</div>
)
}
}
class Feature extends React.Component {
render() {
var bgImg = {
backgroundImage: 'url(' + this.props.img + ')'
};
return (
<div className="feature-container" onMouseEnter={this.props.onMouseEnterHandler}>
<div style={bgImg} className="feature-img"></div>
<div className="feature">
<h4 className="feature-issue">Issue {this.props.issue}</h4>
<h1 className="feature-title">{this.props.title}</h1>
<h4 className="feature-copy">{this.props.copy}</h4>
</div>
</div>
)
}
}
this inside the .map callback is undefined. That's why you get an error when trying to access this.onMouseEnterHandler.
Either use an arrow function:
data.features.map((obj, i) => { ... })
or pass this as second argument to .map:
data.features.map(function(obj, i) { ... }, this)

react change the class of list item on click

I have a react element like this:
import React, { PropTypes, Component } from 'react'
class AlbumList extends Component {
constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}
handleClick() {
if(this.state.active){
this.setState({'active': false,'class': 'album'})
}else{
this.setState({'active': true,'class': 'active'})
}
}
render() {
var album_list
const {user} = this.props
if(user.data){
list = user.data.filter(album => album.photos).map((album => {
return <div className={"col-sm-3"} key={album.id}>
<div className={this.state.class} key={album.id} onClick={this.handleClick.bind(this)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive"} src={album.photo.source} />
</div>
</div>
</div>
}))
}
return (
<div className={"container"}>
<div className="row">
{list}
</div>
</div>
)
}
}
export default AlbumList
Here map gives the list of filter data as I wanted. Here what I am doing changes the class of all the list element if I click on one.
I am getting the class name from this.state.class
How can I change the class of only element that i have clicked..
Thanks in advance ...
I have considered it once.So you have so many divs and you want to know which is clicked.My way to solve this problem is to give a param to the function handleClick and you can get the dom of the div while you click the div.Like this:
array.map(function(album,index){
return <div onClick={this.handleClick}/>
})
handleClick(e){
console.log(e.target);
e.target.className = 'active';
...
}
Then you have a param for this function.While you can use the e.target to get the dom of your div which is clicked.
There are some mistake into your code about the state.class.
class AlbumList extends Component {
constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}
handleClick(e) {
if(e.target.class === 'active'){
e.target.className = 'album'
}else{
e.target.className = 'active'
}
}
render() {
var album_list
const {user} = this.props
if(user.data){
list = user.data.filter(album => album.photos).map((album => {
return (
<div className={"col-sm-3"} key={album.id}>
<div className='active' key={album.id} onClick={this.handleClick.bind(this)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive"} src={album.photo.source} />
</div>
</div>
</div>
)
}))
}
return (
<div className={"container"}>
<div className="row">
{list}
</div>
</div>
)
}
}
You can try this and tell me anything wrong.

Resources