How to remove a component by clicking on another one in React? - reactjs

I'am new to React and I've got a problem. I'm trying to make an app, like Trello clon. So I need to remove a component if I click on the cross of another one.
Here's what I'm trying to do, but definitely it doesn't work. Appreciate any help!
export default class MainPage extends Component{
constructor(props){
super(props);
this.state = {
InputCallState: false
}
this.inputStateHandler = this.inputStateHandler.bind(this);
this.close = this.close.bind(this);
}
inputStateHandler(){
this.setState({
InputCallState: true
});
}
close(){
this.setState({
InputCallState: false
});
}
render(){
return(
<div>
<Link to = '/'><img className = 'icon' src = 'https://s1.iconbird.com/ico/2013/3/635/w176h2521393885387122.png' alt = '' /></Link>
<div onClick = {this.inputStateHandler} className = 'container'>
<span onClick = {this.close} className = "close"></span>
<h1 >Create a board...</h1>
</div>
{this.state.InputCallState ? <AddForm /> : <div></div>}
</div>
);
}
}
<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>

For your close method, stopPropagation since its nested and effects the toggle:
close(e) {
e.stopPropagation();
...
Also, if you don't want an empty div to render, you can instead write
{this.state.InputCallState && <AddForm /> }

Related

React state doesn't seem to be fetching api and setting state before render

I'm trying to fetch an api of quotes and populate the react component with the first one. Later I'll use the button to pick a random one. I'm just now learning react, my react tutorial in freecodecamp didn't show anything about fetch so I found the code to pull these quotes online. If I add another callback after the this.setState I can console.log and see all the arrays but even with the if statement in the render it doesn't seem to be there in the state to render. What am I missing about setting the state or getting the component to render after the state has set to the array. I have already looked at this stackoverflow question.
class Quotes extends React.Component{
constructor(props){
super(props)
this.state = {
quotes: []
}
}
componentDidMount() {
fetch("https://type.fit/api/quotes")
.then((response) => response.json())
.then(quotesList => {
this.setState({ quotes: quotesList });
});
}
render(){
if (!this.state.quotes) {
return <div />
}
return(
<div>
<p id="text">{this.state.quotes[0].text}</p>
<p id="author">{this.state.quotes[0].author}</p>
<div id="buttons">
<button id="new-quote">New Quote</button>
<a id="tweet-quote" href="#"><i className="fa-brands fa-twitter"></i></a>
</div>
</div>
);
}
}
class QuoteBox extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div id="quote-box">
<Quotes />
</div>
);
}
}
ReactDOM.render(<QuoteBox />, document.getElementById('page-wrapper'))
#page-wrapper{
#quote-box{
display:flex;
height:100vh;
justify-content:center;
align-items:center;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="page-wrapper">
</div>
An empty array [] is not a falsy value, consequently your if does not get triggered and an out of bounds array access is done. Check for array length in your if instead and it will work.
See this thread on StackOverflow which covers truthy and falsy values.
Here your code with the condition within the if changed to
this.state.quotes.length === 0.
class Quotes extends React.Component {
constructor(props) {
super(props);
this.state = {
quotes: [],
};
}
componentDidMount() {
fetch("https://type.fit/api/quotes")
.then((response) => response.json())
.then((quotesList) => {
this.setState({ quotes: quotesList });
});
}
render() {
// check for array length here
if (this.state.quotes.length === 0) {
return <div>Fetching data...</div>;
}
return (
<div>
<p id="text">{this.state.quotes[0].text}</p>
<p id="author">{this.state.quotes[0].author}</p>
<div id="buttons">
<button id="new-quote">New Quote</button>
<a id="tweet-quote" href="#">
<i className="fa-brands fa-twitter"></i>
</a>
</div>
</div>
);
}
}
class QuoteBox extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="quote-box">
<Quotes />
</div>
);
}
}
ReactDOM.render(<QuoteBox />, document.getElementById("page-wrapper"));
#page-wrapper{
#quote-box{
display:flex;
height:100vh;
justify-content:center;
align-items:center;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="page-wrapper">
</div>

binding this and sending to handler

I know I can bind this in a couple of different ways in react like these:
<button onClick={this.onSelect.bind(this, data)}>Button</button>
or
<button onClick={() => this.onSelect(data)}>Button</button>
or
constructor(props) {
super(props);
this.onSelect= this.onSelect.bind(this)
}
<button onClick={this.onSelect}>Button</button>
The problem is that I cannot send data using third option. Is there anyway to use the third option and send data as well?
class App extends React.Component {
constructor(props) {
super(props);
this.onSelect = this.onSelect.bind(this);
}
onSelect(e) {
alert(e);
}
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={event => this.onSelect(event)}>Button</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, document.body);
<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 can use ES6 Arrow Function to send data here, it is called anonymous function calling:
constructor(props) {
super(props);
this.onSelect= this.onSelect.bind(this)
}
<button onClick={(event) => this.onSelect(event)}>Button</button>
Like this.
With the new arrow function brought by the ES6 syntax, you can forget about bind.
An arrow function will automatically bind to it's class context. Making your onSelect an arrow function will solve your problem.
To send your data, you can preconfigure your function to receive 2 sets of parameters, the first one being your data, and the second being the click event :
class App extends React.Component {
constructor(props) {
super(props);
this.state = { msg: '' }
}
onSelect = msg => event => {
this.setState({ msg })
}
render() {
const { msg } = this.state
return (
<div className="App">
<h3>{msg ? 'Yay, data : ' + msg : 'Click it !'}</h3>
<button onClick={this.onSelect('I sent data !')}>Button</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.3.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.3.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

innerHTML returns old value instead of a new one

I have a content editable div and an event that is attached to a button and triggers when clicked.
When it is triggered it writes innerHTML of that div but I don't know why with old value.
class Btn extends React.Component {
constructor(){
super();
this.state = { clicked: false };
}
onClick = e => {
console.log(document.getElementById('typer').toString());
if(this.state.clicked && this.props.cmd === 'heading'){
document.execCommand('formatBlock', false, 'p');
} else {
document.execCommand('formatBlock', false, this.props.arg);
document.execCommand(this.props.cmd, false, this.props.arg);
}
this.setState((state, props) => ({clicked: !state.clicked}));
}
render(){
return <button onClick={this.onClick} id={this.props.id}>btn</button>;
}
}
const root = document.querySelector('#app');
ReactDOM.render(
<div>
<nav>
<Btn cmd="bold" name="bold"/>
<Btn cmd="italic" name="italic"/>
<Btn id="big" cmd="heading" name="heading" arg="H1"/>
<Btn id="sml" cmd="heading" name="heading" arg="H2"/>
</nav>
<TyperArea/>
</div>, root
)
nav{text-align:center;border-bottom:2px solid black;top:0;position:sticky;width:100%;background:white}button{vertical-align:middle;display:inline-block;font-size:25px;border-style:none;padding:6px;margin:0 10px;background-color:white;cursor:pointer}button:hover{color:darkslategrey}body{margin:0;width:100%}div[contentEditable=true]{height:10em;width:700px;padding:20px;outline:none;font-family:Georgia;margin:0 auto;font-size:27px}h1,h2{font-family:arial;font-weight:bold}h1{font-size:42px}h2{font-size:35px}#sml{font-size:26px}#big{font-size:30px}
<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>
Binding is necessary to make 'this' work in the callback
constructor(){
super();
this.state = { clicked: false };
this.onClick = this.onClick.bind(this);
}
Read More

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' />

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

Resources