How to refer a variable to the child component with React? - reactjs

class Main extends Component{
/*Skip function and constructor...*/
var previewContainer = this.refs.localMedia;
previewContainer.appendChild("<div>Test</div>");
/*Skip some code...*/
render() {
return (
<div className="container my-4">
<div className="row">
<div className="col">
<div>Screen section</div>
<div className="screen_section">
{this.state.showMyScreen ? <MyScreen localMediaAvailable={this.state.localMediaAvailable} /> : null }
</div>
</div>
</div>
</div>
)
}
}
class MyScreen extends Component {
constructor(props) {
super(props)
}
render() {
let showLocalTrack = this.props.localMediaAvailable ? (
<div className="flex-item"><div ref="localMedia" style={{height:320, width:640}} /> </div>) : '';
return (
<div>
<div>My Screen</div>
{showLocalTrack}
</div>
)
}
}
I write some react code as above, and I have an error message of fail to appendChild of undefined, the problem is I cannot refer the variable with var previewContainer = this.refs.localMedia;
Is it possible to have any method to solve this problem? Or instead of serate them into two component, I should write them in one instead?

Related

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

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>);
}

ReactJS product id is not defined

I have the problem when I use the Reactjs, I'm really new to Reactjs, so maybe it's a easy problem:
class Product extends Component{
handleUpVote() {
this.props.onVote(this.props.id)
}
render(){
return(
<div className='item'>
<div className='middle aligned content'>
<div className='header'>
<a onClick={this.handleUpVote}>
<i className='large caret up icon'></i>
</a>
{this.props.votes}
</div>
</div>
</div>
)
}
}
class ProductList extends Component {
handleProductUpVote(prductId){
console.log(productId +' was upvoted')
}
render() {
const products1 = Data.sort((a,b) => (
b.votes-a.votes
));
const products=products1.map((product) =>{
return (
<div className='ui items'>
<Product
key={'product-'+product.id}
id={product.id}
onVote={this.handleProductUpVote}
/>
</div>
)})
return (
<div className='ui items'>
{products}
</div>
)
}}
export default ProductList;
At this line I am getting the error and i cant understand why:
this.props.onVote(this.props.id)
Forget binding this.
You need to pass the ID as a parameter to your click handler function.
Something like this:
class Product extends Component{
handleUpVote(id) {
this.props.onVote(id)
}
...
<Product
key={'product-'+product.id}
id={product.id}
onVote={() => this.handleProductUpVote(product.id)}
/>
}

React: state-based rendering

I have the following React component:
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
isRendered: false
}
}
render() {
return (
<div className="result">
Rendering result
</div>
)
}
}
Based on the state of this.state.isRendered, I want my <div> component to render if the state is true, and not render if the state is false.
What would be the best way to organize it in React?
Just check this.state.isRendered in the render. Here I used a simple AND check. Click on the button to change state and see how it works (snippet takes a short while to run):
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
isRendered: false
}
}
render() {
const {
isRendered
} = this.state;
return (
<div>
{isRendered &&
<div className="result">
Rendering result
</div>
}
<button onClick={() => {this.setState({isRendered: !isRendered})}}>{isRendered? 'Hide' : 'Show'}</button>
</div>
)
}
}
ReactDOM.render(<Parent />, 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"></div>
Using a ternary operator check whether isRendered is true else return null:
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
isRendered: false
}
}
render() {
return (
<div>
{this.state.isRendered ?
<div className="result">
Rendering result
</div> : null
}
</div>
)
}
}
You are allowed to return null or false for React components. So, if you don't want anything to get rendered, you could do the following:
...
render() {
if (!this.state.isRendered) return null
return (
<div className="result">
Rendering result
</div>
)
}
or, alternatively
...
render() {
return this.state.isRendered && (
<div className="result">
Rendering result
</div>
)
}
You can use simple conditional ternary operator to do this:
condition ? if_true_result : if_false_result
Your code should be like this:
render() {
const {isRendered} = this.state;
return isRendered ?
<div className="result">
Rendering result
</div>
: ''
}
Another official way is following the guidelines about Conditional Rendering in Reactjs document: https://reactjs.org/docs/conditional-rendering.html
There are many ways to accomplish that
{ this.state.isRendered ?
<div className="result">
This if the isRendered is true
</div>
: <div className="result">
This if the isRendered is false
</div> }

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>

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