Is there any reason not to pass react components as properties? - reactjs

Lets say we have a react component called ThreeModule which oh so cleverly displays three arbitary react components, (or even just arbitarily defined dom elements) in a row.
We can do this by setting react elements as state elements and passing them in as properties, as suggested in this answer:
class ModA extends React.Component{
render () {
return <div> this is ModA</div>
}
}
class ModB extends React.Component{
render () {
return <div> this is ModB</div>
}
}
class ModC extends React.Component{
render () {
return <div> this is ModC</div>
}
}
class ThreeMod extends React.Component{
render() {
return (<div className = "ThreeMod">
This is ThreeMod
<div className ="left">
{this.props.moda}
</div>
<div className ="middle">
{this.props.modb}
</div>
<div className ="right">
{this.props.modc}
</div>
</div> );
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
moda: <ModA/> ,
modb: <ModB/> ,
modc: <ModC/>
}
}
render() {
function getRandomMod() {
let rand =Math.floor(Math.random() * Math.floor(4));
switch (rand) {
case 0 : return <ModA/>
case 1: return <ModB/>
case 2: return <ModC/>
default: return "random text";
}
}
return (
<div className="App">
This is App
<button onClick = {() => {
this.setState({moda: getRandomMod() }
)}}>randomise left </button>
<button onClick = {() => {
this.setState({modb: getRandomMod() }
)}}>randomise middle </button>
<button onClick = {() => {
this.setState({modc: getRandomMod() }
)}}>randomise right</button>
<ThreeMod
moda={this.state.moda}
modb={this.state.modb}
modc={this.state.modc}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.body
);
div {
border: solid 1px black;
padding: 0.2em;
}
.ThreeMod {
display:flex;
flex-flow: row;
}
<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 = "react"/>
This works precisely for what I'm wanting to do - what I'm wondering is - is this the wrong way to do things? Is there a reason (eg. performance) not to do things this way?

I personally don't use this approach, but the presence of element ("a React element") and node ("anything that can be rendered") in the PropTypes documentation suggests that it's supported functionality.

Related

Learning react with a click game, onClick won't fire. I think I'm just passing a prop *called* onClick

The Bit component is supposed to be my clickable, which should be incrementing the state due to my mine function in the Mine component.
function Bit(props) {
return (
<img src={logo} className="App-logo" alt="logo" onClick={props.onClick} />
)
}
class Mine extends React.Component {
constructor(props) {
super(props);
this.state = {
bitCoins: 0,
clickBonus: 1,
cps: 1,
}
}
mine() {
alert('here')
this.setState({
bitCoins: this.state.bitCoins + 1
})
console.log(this.state.bitCoins);
}
render() {
let status;
status = this.state.bitCoins
return (
<div>
<Bit onClick={() => this.mine()} />
</div>
<div className="text-primary">{status}</div>
)
}
}
What is returned from render in React cannot have sibling elements at the top level. So just wrapping what you're returning with <React.Fragment> (or a div or whatever else you choose) fixed it.
Also note that setState is asynchronous, so when you console.log immediately after calling it, you may not get the most up to date values.
class Mine extends React.Component {
constructor(props) {
super(props);
this.state = {
bitCoins: 0,
clickBonus: 1,
cps: 1,
}
}
mine() {
alert('here')
this.setState({
bitCoins: this.state.bitCoins + 1
})
console.log(this.state.bitCoins);
}
render() {
let status;
status = this.state.bitCoins
return (
<React.Fragment>
<div>
<button onClick={() => this.mine()}>Mine</button>
</div>
<div className="text-primary">{status}</div>
</React.Fragment>
)
}
}
ReactDOM.render(
<Mine />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How to return element in react class functions

How to return element in react class functions on a click. is it even possible?
class Item extends Component {
constructor(props) {
super(props);
this.itemInfo = this.itemInfo.bind(this);
}
itemInfo = () =>{
return <div> some info</div>
}
render(){
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
</div>
)
}
}
class Item extends React.Component {
state = {
showDiv: false
};
render() {
return (
<div>
<div
style={{ cursor: "pointer" }}
onClick={() =>
this.setState(prevState => ({
showDiv: !prevState.showDiv
}))
}
>
Click Me
</div>
{/*Show the INFO DIV ONLY IF THE REQUIRED STATE IS TRUE*/}
{this.state.showDiv && <InfoDiv />}
</div>
);
}
}
//This is the div which we want on click
var InfoDiv = () => (
<div style={{ border: "2px solid blue",borderRadius:10, padding: 20 }}>
<p> Long Text DIVLong Text DIVLong Text DIVLong Text DIVLong Text DIV </p>
</div>
);
ReactDOM.render(<Item />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You should do that in the state.
itemInfo = () =>{
this.setState({ component:<div> some info</div> });
}
and render the component like this
return(
<div>
<div onClick={this.itemInfo}> Click Here <div>
{this.state.component}
</div>
)
You can try something like this, using the state and conditional rendering:
class Item extends Component {
constructor(props) {
super(props)
this.state = {
showMore: false,
}
}
toggleShowMore = () => {
this.setState({ showMore: !this.state.showMore })
}
render() {
return (
<div>
<div onClick={this.toggleShowMore}>
{this.state.showMore ? 'Show less' : 'Show more'}
</div>
{this.state.showMore ? <div>some info</div> : null}
</div>
)
}
}
Here's how I would do it:
function ItemInfo() {
return(
<div>Some Info</div>
);
}
class Item extends Component {
constructor(props) {
super(props);
this.handleClick= this.handleClick.bind(this);
this.state = {
showInfo: false
}
}
handleClick() {
this.setState((prevState) => {showInfo: !prevState.showInfo});
}
render(){
return(
<div>
<div onClick={this.handleClick}> Click Here <div>
{ this.state.showInfo ?
<ItemInfo/>
: null }
</div>
)
}
}

Render a string which contains components

I am newbie at reactJs and i am trying to build an app in which i get some results after searching .
My problem is that i have a component called ResultEntity and I am trying create a dynamically page without defined number of ResultEntity components.
I tried something like this
for(var i=0 ; i<result.length ; i++)
{
results += "<div> <ResultEntity/> </div>";
};
console.log(results);
this.setState({result: results});
And i tried to return it like ,
return (
<div>
<div dangerouslySetInnerHTML={{ __html: this.state.result }} />
</div>
);
and
return (
<div>
<div dangerouslySetInnerHTML={{ __html: this.state.result }} />
</div>
);
but both didnt work . Any idea will be appreciated . Thank you in advance
So you want to render a list of components dynamically. Here's how you can do it using .map function:
// This also can be a functional component, instead of a class
class ResultEntity extends React.Component {
render() {
const { item } = this.props
return <div>{ `${item.id} - ${item.name}` }</div>
}
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
items: [
{ id: 1, name: 'Bulgaria' },
{ id: 2, name: 'Germany' },
]
}
}
renderItems() {
const { items } = this.state
// Great explanations, why this work and it's rendered correctly:
// https://medium.com/byte-sized-react/component-arrays-in-react-a46e775fae7b
return items.map(item => <ResultEntity key={item.id} item={item} />)
}
render() {
// From React >= 16 it's possible to skip the wrapper `div`:
// https://stackoverflow.com/a/32157488/4312466
return <div>{ this.renderItems() }</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<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="container">
<!-- This element's contents will be replaced with your component. -->
</div>

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 invoke props function passed to children in a loop reactjs

I am new to react. I am just trying to create a comment box and comment board which contain multiple comments.
Each comments have one inputbox, button(save,edit) and button(remove). I have passed function made in board named updateComment to Component Comment as props.
Now When I am trying to execute save of child function in which I have called parent function updateComment using this.props.updateComment
it is giving me error can't read property of undefined.
I have searched for similar question on stackoverflow but I am unable to solved this proplem.
My app.js code is as below.
import React from 'react';
import { Home } from './home.jsx';
class App extends React.Component {
render() {
return (
<div>
<Header/>
<Board />
</div>
);
}
}
class Header extends React.Component {
render() {
return (
<div>
<h1>Header</h1>
</div>
);
}
}
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
comments:[
"My name is brijesh",
"My name is santosh",
"My name is manoj"
]}
};
removeComment(i) {
console.log("going to remove element i",i);
var arr = this.state.comments;
arr.splice(i,1);
this.setState({comments:arr});
};
updateComment(newComment, i) {
var arr = this.state.comments;
arr[i] = newComment;
this.setState({comments:arr});
};
render() {
return (
<div className="board">
{
this.state.comments.map(function(text,i) {
return (
<Comment key ={i} index = {i}
updateComment={() => {this.updateComment}}
removeComment={() => {this.removeComment}}>
{text}
</Comment>
)
})
}
</div>
)
}
}
class Comment extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: false
};
};
edit(){
this.setState({edit:true});
console.log("you clickced on edit0");
};
save(){
this.setState({edit:false});
var newText = this.refs.newText.value;
this.props.updateComment(newText, this.props.index);
console.log("you clickced on edit0",newText);
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
if(this.state.edit) {
return (
<div>
<div className="comment">
<input type="text" ref="newText" defaultValue={this.props.children} onChange={ this.handleChange.bind(this) } />
<button onClick={this.save.bind(this)}>Save</button>
</div>
</div>
)
}
else {
return (
<div>
<div className="comment">
<div>{ this.props.children }</div>
<button onClick={this.edit.bind(this)}>Edit</button>
</div>
</div>
)
}
}
}
export default App
And my main.js looks like this.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(
( < App / > ), document.getElementById('app'));
I have also created fiddle also.
https://jsfiddle.net/aubrijesh/k3h2pcnj/#&togetherjs=uEI7TFnJD1
I believe that DOMZE is on the right track but you should also bind the function in the map statement. In my opinion arrow functions makes it much easier to keep track of what this refers to.
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
comments:[
"My name is brijesh",
"My name is santosh",
"My name is manoj"
]}
};
removeCommment(i) {
console.log("going to remove element i",i);
var arr = this.state.comments;
arr.splice(i,1);
this.setState({comments:arr});
};
updateComment(newComment, i) {
var arr = this.state.comments;
console.log("new Comment");
arr[i] = newComment;
this.setState({comments:arr});
};
render() {
return (
<div className="board">
{
this.state.comments.map((text,i) => {
return (
<Comment key ={i} index = {i}
updateComment={() => {this.updateComment}}
removeComment={() => {this.removeComment}}>
{text}
</Comment>
)
})
}
</div>
)
}
}
class Comment extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: false
};
};
edit(){
this.setState({edit:true});
console.log("you clickced on edit0");
};
save(){
this.setState({edit:false});
var newText = this.refs.newText.value;
this.props.updateComment(newText, this.props.index);
console.log("you clickced on edit0",newText);
};
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
if(this.state.edit) {
return (
<div>
<div className="comment">
<input type="text" ref="newText" defaultValue={this.props.children} onChange={ this.handleChange} />
<button onClick={this.save.bind(this)}>Save</button>
</div>
</div>
)
}
else {
return (
<div>
<div className="comment">
<div>{ this.props.children }</div>
<button onClick={this.edit.bind(this)}>Edit</button>
</div>
</div>
)
}
}
}
ReactDOM.render(<Board />, 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>
update your render method
let self = this;
return (
<div className="board">
{
self.state.comments.map(function(text,i) {
return (
<Comment key ={i} index = {i}
updateComment={() => {self.updateComment}}
removeComment={() => {self.removeComment}}>
{text}
</Comment>
)
})
}
</div>
)
You need to bind the class to the function, so that it knows what "this" is
render() {
return (
<div className="board">
{
this.state.comments.map(function(text,i) {
return (
<Comment key ={i} index = {i}
updateComment={this.updateComment.bind(this)}
removeComment={this.removeComment.bind(this)}>
{text}
</Comment>
)
})
}
</div>
)
}
Note that you may want to do those bindings in the constructor so that it doesn't bind at each and every render

Resources