reactjs conditional render works after second click - reactjs

I have three components: MyDashboard, MyContent, MyForm.
The MyDashboard component starts off by rendering the MyContent component as a child. I have a button within the MyContent component which when clicked should swap out the component with the MyForm component.
Clicking the button flashes the MyForm component on the first click, then goes straight back to the MyContent component. After the second click however the MyForm component is rendered and stays rendered.
MyDashboard:
function MyDashboard(props) {
const [viewForm, setViewForm] = useState(false); // Don't show the form initially
const handleClick = () => {
setViewForm(!viewForm);
};
return (
<div>
{(() => {
if (viewForm) { // conditionally render the form or the content
return <MyForm {...props} />;
} else {
return (
<MyContent
{...props}
setViewForm={handleClick}
viewForm={viewForm}
/>
);
}
})()}
</div>
);
}
MyContent:
function MyContent(props) {
return (
<p>
<a href="#" onClick={props.setViewForm}> // need to click this twice to render the form.
Show the form
</a>
</p>
);
}
export default MyContent;
MyForm:
function MyForm({ props, location }) {
// form setup
return (
<div>
<Row>
<Form>
//... the form
</form>
</Row>
</div>
);
}
export default MyForm;

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 hide a component in react?

I have two components App component and the Component2
import Component2 from './Component2';
class App extends Component {
state={data:""}
changeState = () => {
this.setState({data:`state/props of parent component
is send by onClick event to another component`});
};
render(){
return (
<div className="App">
<Component2 data={this.state.data} />
<div className="main-cointainer">
<h2>Compnent1</h2>
<button onClick={this.changeState} type="button">
Send state
</button>
</div>
</div>
);
}}
export default App;
and the Component2.js
const Component2 = (props) => {
return (
<div className="main-cointainer">
<h2>Compnent2</h2>
<p>{props.data} </p>
</div>
)
}
export default Component2;
Here I am passing the data from App component to the Component2 component . What I want is to hide
the Component2 from being rendered while passing the data from App component to the Component2 component .
I tried writing this statement
{ false && <Component2 data={this.state.data} /> }
but this did not work out .
Any suggestions to achieve this type of requirement ?
To hide Component2 (without changing its functionality), add it in a div with style = {{display: 'none'}}
App.js
import React from "react";
import "./style.css";
import Component2 from "./Component2";
export default function App() {
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
<div style={{ display: "none" }}>
<Component2 myProps="this is a example" />
</div>
</div>
);
}
Check demo : https://stackblitz.com/edit/react-bbxgcr?file=src%2FApp.js
PS : Open the console to see the props in correctly passed to Component2
you can do this with only one if. If I understood the problem correctly
const Component2 = (props) => {
if(props.data === ""){
return(<div></div>)
}
return (
<div className="main-cointainer">
<h2>Compnent2</h2>
<p>{props.data} </p>
</div>
)
}
export default Component2;
Try using negation to simply convert an empty string into a boolean value.
{ !!this.stat.data && <Component2 data={this.state.data} /> }
When the this.stat.data is an empty string or falsy value then the Component2 will not render
Or
If you don't want to show display component on the page try this
<Component2 data={this.state.data} hidden={true}/>
Component2
const Component2 =({ data, hidden }) => {
return (
<div hidden={hidden}>
<h1>Component2</h1>
</div>
);
}
Adding hidden attribute on the HTML tag will hide the element on the page.

how to disable a button in React imported into another component?

it's my first application in react and I'm not sure how to disable an imported button.
I have a component button that I import into a parent component
import React, { Component } from "react";
import "../../index.scss";
class Submit extends Component {
render() {
return (
<button className="button"
onClick={() => this.props.onClick()}>
SUBMIT
</button>
);
}
}
export default Submit;
in the component that rendered it is as follows
renderSubmit() {
return (
<Submit
onClick={() => this.submitForm()}
/>
);
}
render() {
return (
<div className="table">
<div className="table-actions">
{this.renderRefresh()}
{this.renderSubmit()}
</div>
</div>
);
}
}
I have tried to set the class to disabled from the original component but it depends on a state property and does not recognize it.
import React, { Component } from "react";
import "../../index.scss";
class Submit extends Component {
render() {
return (
<button className="button"
disabled={this.state.start}
onClick={() => this.props.onClick()}>
SUBMIT
</button>
);
}
}
export default Submit;
How can I condition the disabled state to a state property?
Your Submit button doesn't allow for setting any other props on the underlying button component. It should proxy though any props you want to be externally configured by what is rendering the Submit button. I also suggest explicitly declaring the button type to be "submit", or also exposing that prop out in the component API.
Your proxying of the onClick handler also drops the click event, that should be passed through in case any consuming component care about it.
class Submit extends Component {
render() {
const { disabled, onClick, type = "submit" } = this.props;
return (
<button
className="button"
disabled={disabled}
onClick={onClick}
type={type}
>
SUBMIT
</button>
);
}
}
For such a simple component with no internal logic IMO a functional component is a better option, and I would name it more clearly.
const SubmitButton = ({ disabled, onClick, type = "submit" }) => (
<button
className="button"
disabled={disabled}
onClick={onClick}
type={type}
>
SUBMIT
</button>
);
Now when you are using the submit button from a parent component you can pass in a disabled prop based on any condition you need/require.
render() {
const { submitDisabled } = this.state;
return (
<div className="table">
<div className="table-actions">
{this.renderRefresh()}
<SubmitButton
disabled={submitDisabled} // <-- pass disabled value
onClick={this.submitForm} // <-- attach click handler
type="button" // <-- so we don't accidentally take default form action
/>
</div>
</div>
);
}
}
How you compute/set this.state.submitDisabled is up to you. Maybe it is disabled when the form is being submitted, for example.
submitForm = () => {
this.setState({ submitDisabled: true });
...
};

How to load new component on button click

I have a form, when user taps on Login button, i want to load a new component, and disable the current component. I read about react-router,but it looks like i can use them for navigation.
This is my code.
class App extends Component {
render() {
return (
<div className="App">
<HeaderComponent />
<header className="App-header">
<LoginComponent/>
</header>
</div>
);
}
when user taps on Login button, this method in LoginComponent.js gets called.
handleSubmit = event => {
event.preventDefault();
console.log("Login");
}
Now i can successfully prints the Log message, but i am unable to understand how to load a different component at this time.
If you don't want it to be on a separate page you can use a conditional in your render, and change the state on the submit.
Example:
class App extends Component {
state = {
componentShown: false
}
render() {
return (
<div className="App">
<HeaderComponent />
<header className="App-header">
{this.state.componentShown ? <ComponentToRender /> :
<LoginComponent/> }
</header>
</div>
);
}
And on your submit just change the state.
handleSubmit = event => {
event.preventDefault();
this.setState({ componentShown: true })
}

react stateless component with onClick issue

I have a stateless component and i want to be able to click on the image and redirect it to a new page.
The issue that i am having is that i cannot get onClick to work correctly.
I was trying to write a function within the onClick = {this.ProjectSelected} but this will not work.
Do i have to pass a function (onClick) from the parent to child component? If so how do i do that?
I am able to log the project id to the console.
Thanks for the help.
const projectListTemplates = (props) => {
const { classes } = props;
return (
<div>
<div className={classes.root}>
<GridList className={classes.gridList}>
<GridListTile key="Subheader" cols={2} style={{ height: 'auto' }}>
<Subheader component="div">Projects</Subheader>
</GridListTile>
{props.data.map(item => (
<GridListTile key={item.image}>
<img src="./images/Project/1.jpg" alt={item.title} />
<GridListTileBar
title={item.title}
subtitle={<span> {item.name}</span>}
onClick={()=>console.log(`this project was clicked ${item.id}`)}
>
</GridListTileBar>
/>
<ProgressBar now={60} />
</GridListTile>
))}
</GridList>
</div>
</div>
);
In stateless component we are not defining any local property like state or methods. We have only props and rendering data based on props. We have only props events to trigger. So we have to pass a event function from parent component in order to handle click event. If you want to keep click event locally, convert component to stateful (Class).
Stateless
const ProjectListComponent = (props) => {
const { onClick } = props
return (
<button onClick={onClick}>Click me</button>
)
}
class AppComponent extends Component {
handleClick() {
console.log('clicked')
}
render() {
return (
<ProjectListComponent onClick={this.handleClick} />
)
}
}
Stateful
class ProjectListComponent extends Component {
handleClick() {
console.log('clicked')
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
)
}
}

Resources