Rendering a Bootstrap Component Using JSX - reactjs

Editing for clarity: I cannot figure out how to dynamically create Boostrap Components using JSX in a react app. End goal is to get the new button in the "newBtnSpace" div when the first button is clicked. I have tried using show.hide methods, but those need to be hard coded. Trying to create buttons based off an array. code:
./components/newBSBtnSpaceFunc.js
import React, { Component } from 'react'
import { Button } from 'reactstrap'
export default function NewBSBtnFunc() {
let BtnArray = ["red", "blue", "green"].map((btn) => {
return React.createElement(
Button,
{variant: 'primary'},
'New Button',
{id: "newBtn"},
btn
)
}
./components/BSBtn.js
import React, { Component } from 'react'
import { Button } from 'reactstrap'
import NewBSBtnFunc from "./NewBSBtnFunc"
export default class BSBtn extends Component {
render() {
return (
<div>
<Button onClick={NewBSBtnFunc}>Click Me</Button>
<div id="newBtnSpace"></div>
</div>
)
}
}
App.js
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import BSBtn from "./components/BSBtn"
function App() {
return (
<div>
<BSBtn></BSBtn>
</div>
);
}
export default App;
github link: https://github.com/mollygilbert389/testingBootstrapBtn

You can conditionally show the new button by setting a state item (in this case showNewButton) to true in the onClick of the original button.
render() {
return (
<div>
<Button onClick={() => this.setState({ showNewButton: true }))}>Click Me</Button>
<div id="newBtnSpace">{ this.state.showNewButton && <Button variant="primary" id="newBtn">New Button</Button> }</div>
</div>
)
}
PS you've already successfully worked out how to create Bootstrap buttons in jsx:
<Button onClick={NewBSBtnFunc}>Click Me</Button>

onClick does not expect a return value so returning the new button won't do anything.
The way you have things organized makes it very difficult since you can't return anything from the function, and you can't modify state from outside the class. I would suggest moving your click handler into the component and using to to modify a state value that will show the second button.
Here is my suggestion:
import React, { Component } from 'react'
import { Button } from 'reactstrap'
export default class BSBtn extends Component {
state = {show: false}
handleClick = () => {
this.setState({ show: !this.state.show })
}
render() {
return (
<div>
<Button onClick={this.handleClick}>Click Me</Button>
<div id="newBtnSpace">
{this.state.show ?
<Button variant="primary" id="newBtn">New Button</Button>
: null}
</div>
</div>
)
}
}
Updated solution to your updated question:
class BSBtn extends React.Component {
state = {
show: false,
buttons: []
}
handleClick = () => {
this.setState({ show: !this.state.show })
}
handleAdd = () => {
this.setState({ buttons: [...this.state.buttons, (this.state.buttons.length + 1)] })
}
render() {
return (
<div>
<h3>Option 1</h3>
<button onClick={this.handleClick}>Click Me</button>
<div id="newBtnSpace">
{this.state.show ? [1,2,3].map((value) => (
<div>
<button>Button {value}</button>
</div>
))
: null}
</div>
<hr/>
<div style={{ marginTop: '30px' }}>
<h3>Option 2</h3>
<button onClick={this.handleAdd}>Click Me</button>
{this.state.buttons.map((value) => (
<div>
<button>Button {value}</button>
</div>
))}
</div>
</div>
)
}
}
ReactDOM.render(<BSBtn />, 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' />

Related

Why do createPortal renders the whole component?

I have a problem with the createPortal function. When I use the "Modal", then by changing the states, the whole "Modal" component will be rendered. Can you help me how to avoid this? So when I comment out the Modal wrap in the Cart component (as I did it below), it works as I expected, but with the Modal wrap, when the states are changed, not the component will be re-rendered, but always the whole Modal Component
Here is my Modal component with createPortal function:
import React from 'react';
import { createPortal } from 'react-dom';
import { useState } from 'react';
import './modal.scss';
export default function Modal({ children, onClick }) {
const BackDrop = () => {
return <div className='backdrop' onClick={onClick}></div>;
};
const Modal = () => {
return (
<div className='modal'>
<div className='content'>{children}</div>
</div>
);
};
return (
<>
{createPortal(<BackDrop />, document.getElementById('modal'))}
{createPortal(<Modal />, document.getElementById('modal'))}
</>
);
}
The Cart component which uses the Modal component:
import React, { useEffect } from 'react';
import Modal from '../UI/Modal';
import './cart.scss';
import { useCartContext } from '../../store/CartContext';
import CartItem from './CartItem';
export default function Cart({ onHideCart }) {
const { cart, totalPrice, updateTotalPrice, addToCartOne, removeFromCartOne } = useCartContext();
useEffect(() => {
updateTotalPrice();
}, [totalPrice, cart]);
const onAddHandler = (id) => {
addToCartOne(id);
updateTotalPrice();
};
const onRemoveHandler = (id) => {
removeFromCartOne(id);
updateTotalPrice();
};
return (
// <Modal onClick={onHideCart}>
<div>
<ul className='cart-items'>
{cart.map((item, idx) => (
<CartItem
key={item.id}
name={item.name}
price={item.price}
amount={item.amount}
onAdd={onAddHandler.bind(null, item.id)}
onRemove={onRemoveHandler.bind(null, item.id)}
/>
))}
</ul>
<div className='total'>
<span>Total Amount</span>
<span>$ {totalPrice.toFixed(2)}</span>
</div>
<div className='actions'>
<button className='button--alt' onClick={onHideCart}>
Close
</button>
<button className='button'>Order</button>
</div>
</div>
// </Modal>
);
}
So by changing the amount with + and - buttons, the html element with id modal, always renders, it's flashes in the devtools... but when I comment out the Modal wrap in the Cart component, there is no flashes by modal ID. I hope it makes sense.
The problem was with the two custom element inside of the Cart element. When I return
createPortal(
<>
<div className='backdrop' onClick={onClick}></div>
<div className='modal'>
<div className='content'>{children}</div>
</div>
</>,
document.getElementById('modal')
)
instead of this:
<>
{createPortal(<BackDrop />, document.getElementById('modal'))}
{createPortal(<Modal />, document.getElementById('modal'))}
</>
Then there is no problem with rendering.

How to set state properly in react js

I have 2 react components -
import React, {Component} from 'react'
import './App.css';
import ContactCard from './components/ContactCard'
class App extends Component {
render() {
return (
<div className="App-header">
<ContactCard name="hi 1" age={36} email="hu#yahoo.com"/>
<ContactCard name="hi 2" age={67} email="hi#yahoo.com"/>
<ContactCard name="hi 2" age={42} email="he#yahoo.com"/>
</div>
);
}
}
export default App;
import React, { Component } from "react";
class ContactCard extends Component {
state = {
showAge: false,
};
setAge = () => {
this.setState({
showAge: !this.state.showAge,
});
};
render() {
return (
<div className="contactCard">
<div className="userDetails">
<h2>Name: {this.props.name}</h2>
<p>Email: {this.props.email}</p>
<button onClick={this.state.setAge}>Show Age</button>
{this.state.showAge && <p>Age: {this.props.age}</p>}
</div>
</div>
);
}
}
export default ContactCard;
Toggle button is not working. i have set the state before render method. its not mandatory to set the state in the constructor.
Now the error is gone but still toggle button not working.
Whats going wrong?
You are setting state the wrong way. It should be:
setAge = () => {
this.setState({
showAge: !this.state.showAge
});
};
Edit
In order to make your app work as expected, you also have to set the click handler properly:
<button onClick={this.setAge}>Show Age</button>
You have to update your code its this.setAge not this.state.setAge
render() {
return (
<div className="contactCard">
<div className="userDetails">
<h2>Name: {this.props.name}</h2>
<p>Email: {this.props.email}</p>
<button onClick={this.setAge}>Show Age</button>
{this.state.showAge && <p>Age: {this.props.age}</p>}
</div>
</div>
);
}

Handling React onToggle event

I am working on the below snippet, and I'd like to find out why I am not able to bind onToggle event with button component. I am getting 'onToggle' is not defined on compiling.
In the main container (Content) I have:
class Content extends Component {
constructor() {
super();
this.state = {
user: dataService.User
}
}
onTogglePane(){
var node = ReactDOM.findDOMNode(this.refs.wrapper);
node.classList.toggle('toggled');
}
onSignOut() {
dataService.Logout((result) => {
this.setState({
user: null
})
});
}
render() {
return (
<div>
<Header
onClick = {this.onSignOut}
onToggle ={this.onTogglePane}
/>
</div>
)
}
}
In the Button.js I have button component as:
import React from 'react';
const Button = ({ text, styleClass, onClick }) => {
return (
<button
type="button"
onClick={e => onClick(e)}
onToggle={e => onToggle(e)}
className={`btn ${styleClass}`}
>
{text}
</button>
);
};
export default Button;
and finally in the Header.js I have
import React from 'react';
import Button from 'components/Button';
class Header extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<nav className="navbar navbar-default hk-navbar fixed-top">
<p className="navbar-brand tk-brand">App</p>
<Button
text={[<i class="icon icon-logout"></i>, " Sign Out"]}
onClick = {(e) => this.props.onClick(e)}
styleClass = 'btn-control'
/>
<Button
text={[<i class="icon icon-logout"></i>, " Full Screen"]}
onToggle = {(e) => this.props.onToggle(e)}
styleClass = 'btn-control'
/>
</nav>
);
}
}
export default Header;
Now I am getting this error:
Failed to compile
./src/components/Button.js
Line 8: 'onToggle' is not defined no-undef
Search for the keywords to learn more about each error.
Try this, you are missing onToggle which you are passing it to Button component.
Below Button component code would fix the issue
import React from 'react';
const Button = ({ text, styleClass, onClick, onToggle }) => {
return (
<button
type="button"
onClick={onClick ? onClick: null}
onToggle={onToggle ? onToggle: null}
className={`btn ${styleClass}`}
>
{text}
</button>
);
};
export default Button;

How to pass value of button to the function in react

I want that on clicking the button X its value X pass to the result function(same with O) where I can store it in a variable. I don't know how to pass this value on calling result and access it there. I tried to find many answers but none worked.
I'm absolutely new to react. Please help!! Thanks!!
This is code snippet
import React, { Component } from 'react';
import './App.css';
import { Link } from 'react-router-dom';
class App extends Component {
render() {
return (
<div>
<h1 align="center">Welcome to TicTacToe</h1>
<br></br><br></br><br></br><br></br>
<div class="front">
Choose your sign !!
<CheckBox type='submit' value='X' id='x' onSubmit={'how to pass value'}/>
<CheckBox type='submit' value='O' id='o' onSubmit={'how to pass value'}/>
</div>
<br></br>
<Link to= "game"><p class="wrap"><button class="button">GO</button></p></Link>
{this.props.children}
</div>
);
}
}
class CheckBox extends Component{
result(i){
//what to access value
}
render(){
return (
<div className={'check-field'}>
<button type={this.props.type} value={this.props.value} name={this.props.name} id={this.props.id}>{this.props.value}</button>
</div>
);
}
}
export default App;
I'm not sure if I understand your problem properly, but to get a value from a child input component (input/button/textarea) in its parent, just pass a prop with a function which will be called in the child on any onClick/onChange callback function. Here's a little example:
class App extends React.Component {
onSubmit(value) {
// I have the button value here!
console.log(value);
}
render() {
return (
<div>
<Button value="X" onSubmit={this.onSubmit} />
<Button value="O" onSubmit={this.onSubmit} />
</div>
)
}
}
class Button extends React.Component {
onClick(event) {
const value = event.target.value;
this.props.onSubmit(value);
}
render() {
return (
<button value={this.props.value} onClick={e => this.onClick(e)}>
{this.props.value}
</button>
);
}
}
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" />
I hope this solves your problem!
You can store for each checkbox value in state of App. Then, when checkbox is subimtted value it will call handleSubmit and event parameter e holds value and id of checkbox. In your case you can examine if it's X or Y and setState accordingly. This will re-render checkboxes who will via props get proper values.
import React, { Component } from 'react';
import './App.css';
import { Link } from 'react-router-dom';
class App extends Component {
state = {
X : false,
Y : false
}
handleSubmit = (e) => {
if(e.target.id === 'x') {
this.setState({X:true});
} else {
this.setState({Y:true});
}
}
render() {
const { X,Y } = this.state;
return (
<div>
<h1 align="center">Welcome to TicTacToe</h1>
<br></br><br></br><br></br><br></br>
<div class="front">
Choose your sign !!
<CheckBox type='submit' value={X} id='x' onSubmit={this.handleSubmit}/>
<CheckBox type='submit' value={Y} id='o' onSubmit={this.handleSubmit}/>
</div>
<br></br>
<Link to= "game"><p class="wrap"><button class="button">GO</button></p></Link>
{this.props.children}
</div>
);
}
}
class CheckBox extends Component{
render(){
const {value, type, name, id} = this.props;
return (
<div className={'check-field'}>
<button type={type} value={value} name={name} id={id}>{value}</button>
</div>
);
}
}
export default App;

Pass map's argument to function in ReactJS

I am trying to make a todoList by ReactJS. I want to delete an item by its id, but when I console.log(id), it returns undefined. Here is my code
App.js:
import React, { Component } from 'react';
import './App.css';
import Header from './Components/header';
import InputTodo from './Components/todoInput';
class App extends Component {
constructor(props){
super(props);
this.state={
todos:[
{id:0,text:'Make dinner'},
{id:1,text:'Fold the laundary'},
{id:2,text:'Do homework'}
]
}
}
addHere=(text)=>{
this.setState({
todos:this.state.todos.concat([text])
})
}
removeHere=(id)=>{
console.log(id);
// let arr=this.state.todos;
// let index=arr.findIndex((x)=>x.id===id);
// console.log(index);
}
render() {
return (
<div className='todo-wrapper'>
<Header/>
<InputTodo todoText='Type Here...' addTodo={this.addHere}/>
<div>
{this.state.todos.map((value,key)=>{
return (
<div className='row myList' key={key}>
<p className='col-xs-10'> {value.text}-{value.id} </p>
<button className='btn btn-danger pull-right col-xs-2' onClick={this.removeHere(value.id)}>Delete</button>
</div>
)})}
</div>
</div>
);
}
}
export default App;
The following is InputTodo.js:
import React, {Component} from 'react';
import '../App.css';
export default class InputTodo extends Component{
constructor(props){
super(props);
this.state={
todoInput:{
id:2,
text:''
}
}
}
handleSubmit=(e)=>{
if(this.refs.title.value===''){
alert('You must input something');
}
else{
this.state.todoInput={
id:this.state.todoInput.id+1,
text:this.refs.title.value
};
this.setState(this.state);
this.props.addTodo(this.state.todoInput);
this.refs.title.value='';
}
e.preventDefault();
}
render(){
return(
<form className='input-group' onSubmit={this.handleSubmit}>
<input type='text' ref="title" className='form-control'placeholder={this.props.todoText}/>
<span className='input-group-btn'>
<input type='submit' value='Submit' className='btn btn-primary' />
</span>
</form>
);
}
}
While FuzzyTree's answer will work, a cleaner approach would be extracting the todo item's JSX into its own component. This would have the added benefit of not creating a new function for the button's onClick prop every time App's render function gets called.
The component might look like this:
// TodoItem
class TodoItem extends Component {
handleRemove = () => this.props.onRemove(this.props.id)
render() {
return (
<div className='row myList'>
<p className='col-xs-10'> {this.props.text}-{this.props.id} </p>
<button className='btn btn-danger pull-right col-xs-2' onClick={this.handleRemove}> Delete </button>
</div>
)
}
}
// App render
render() {
return (
<div className='todo-wrapper'>
<Header/>
<InputTodo todoText='Type Here...' addTodo={this.addHere}/>
<div>
{this.state.todos.map(({ id, text }, key) =>
<TodoItem key={key} id={id} text={text} onRemove={this.removeHere} />
)}
</div>
</div>
);
}

Resources