I've made many Modals in React.
I found 2 ways of making Modal.
The first one is like this
class Modal extends React.Component {
componentDidMount(){ console.log('DidMount') };
componentDidUpdate(){ console.log('DidUpdate') };
componentWillUnmount(){ console.log('WillUnmount') };
render(){
return (
<React.Fragment>
<div className="overlay" />
<div className="Modal>
This is Modal.
</div>
</React.Fragment>
)
}
}
class App extends React.Component {
state = {
isModalOpen: false
}
toggleModal = () => this.setState({ isModalOpen: !this.state.isModalOpen })
render(){
return (
<div className="App">
<button onClick={this.toggleModal}>Toggle</button>
{ this.state.isModalOpen ? <Modal /> : null }
</div>
)
}
}
This one repeats componentDidMount()&componentWillUnmount() when the state changed.
Let's see the other one.
class Modal extends React.Component {
componentDidMount(){ console.log('DidMount') };
componentDidUpdate(){ console.log('DidUpdate') };
componentWillUnmount(){ console.log('WillUnmount') };
render(){
return (
<React.Fragment>
{ props.isOpen ? <div className="overlay" /> : null }
{ props.isOpen ? <div className="Modal">This is Modal</div> : null }
</React.Fragment>
)
}
}
class App extends React.Component {
state = {
isModalOpen: false
}
toggleModal = () => this.setState({ isModalOpen: !this.state.isModalOpen })
render(){
return (
<div className="App">
<button onClick={this.toggleModal}>Toggle</button>
<Modal isOpen={this.state.isModalOpen} />
</div>
)
}
}
This one would not call componentWillUnmount().
It would call componentDidUpdate() when the state changed.
I wonder which one is a better way for the performance or something else.
Thank you in advance.
React.Fragment is a little bit fast and uses less memory because it doesn't need to create an extra DOM node.
With that being said unless your application is big and complex I wouldn't worry about it. I'm not exactly sure what wrapping the modal div in a React.Fragment is achieving.
You question is a bit confusing but I will attempt to clarify a few things.
Regarding your comment: This one would not call componentWillUnmount(). It will not call the the cWU() method because you are always rendering it by using this <Modal isOpen={this.state.isModalOpen} /> within your render. Now wether it appears or not based on your isOpen prop it's a different issue. On the other hand if you had something like {isThisPropertyTrue ? <Modal isOpen={this.state.isModalOpen} /> : null}, and your isThisPropertyTrue was toggling from true to false, then you would notice the console.log in your unmount hook.
This method { this.state.isModalOpen ? <Modal /> : null } has a better performance since the modal is render upon request.
Related
Expected component behaviour:
- Load animation as defined by 'if' condition
- Then after 1000ms, the setTimeout function will change the state, thus rendering the 'else' part of the conditional
Additional Details
The component loads in fine, however, when I have another instance of the component created, both on alerts fire at the same time, and the this.render.state is set to true. This prevents the animation from loading as part of the conditional statement, since state.render is true... even though it should be false... Shouldn't each instance of this component display the default conditional behaviour?
Name.js Component
export class Name extends Component {
state = {
render: false,
};
componentDidMount() {
//alert('Test')
setTimeout(
function () {
//Start the timer
this.setState({ render: true }); //After 1 second, set render to true
}.bind(this),
1000
);
}
render() {
if (this.state.render === false) {
return (
<div className={styles.LContainer}>
<div className={styles.ldsRing}>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
);
} else {
console.log(this.state.render)
return (
<div className={styles.Container}>
<img
className={styles.imageO}
src={require("./Apple_logo_white.svg")}
/>
<div className={styles.textArea}>
<h1 className={styles.title}>{this.props.title}</h1>
<h2 className={styles.subtitle}>
{this.props.sub}
</h2>
<Input name="name" handleChange = {this.props.handleChange} />
<ScreenButton goDirection={this.props.backScreen} direction="back"/>
<ScreenButton goDirection={this.props.nextScreen} direction="next"/>
</div>
</div>
);
}
}
}
I'm also using a switch statement to render these components, could that have anything to do with it?
render() {
//Pulling information down from state:
const { step, name, interest, location } = this.state;
const values = { step, name, interest, location };
switch (step) {
case 1:
return (
<Welcome
nextScreen={this.nextScreen}
handleChange={this.handleChange}
values={values}
/>
);
case 2:
console.log(this.state.step);
//return <h1>GOLS</h1>
return (
<React.Fragment>
<Name
nextScreen={this.nextScreen}
backScreen={this.backScreen}
handleChange={this.handleChange}
values={values}
sub="Example text"
title="Example text"
/>
</React.Fragment>
);
case 3:
console.log(this.state.step);
return (
<React.Fragment>
<Name
nextScreen={this.nextScreen}
backScreen={this.backScreen}
handleChange={this.handleChange}
values={values}
sub="Example text"
title="Example text"
/>
</React.Fragment>
);
}
}
[1]: https://i.stack.imgur.com/U0v38.gif
I just checked and confirmed it's working like this.
import React, { Component } from 'react';
export default class Name extends Component {
constructor(props){
super();
this.state = {
render: false,
};
}
componentDidMount() {
//alert('Test')
setTimeout(
()=>{
this.setState({ render: true });
},1000
);
}
render() {
if (this.state.render === false) {
return (
<h1>Helloooo</h1>
);
} else {
console.log(this.state.render)
return (
<h1>Bye</h1>
);
}
}
}
See this: https://codesandbox.io/s/angry-breeze-gdmmy?file=/src/App.js
Good news! I figured out the solution when my <Input/> components started to behave similarly.
The problem was that each component (both <Name /> and <Input/>were behaving similarly to their first rendered instance, almost as if they were sharing state.
The fix was simple... assign a key value for each component instance.
Like this:
<Name key="Unique Key Value">
I'm still learning the basics of React, and I wanted to do something that caught my attention. It's about rendering two things. Is it possible or even plausible to just change what is rendered with a function, and then calling the functions separately with a button or timer?
This is a sample code, to show how it would render two completely different things. It is supposed to render a button that says "State: On" or "State: Off". And when you click the button the state changes. But also, the whole render method is switched... or at least that's what's supposed to be happening.
class Flicker{
constructor(props){
super(props);
this.state = {mode: "on"};
}
flipOn(){
this.setState({mode: "on"})
}
flipOff(){
this.setState({mode: "off"})
}
if (this.state.mode == "on"){
render() {
return(
<button onClick={this.flipOn}>State: On</button>
);
}
} else if (this.state.mode == "off"){
render() {
return(
<button onClick={this.flipOff}>State: Off</button>
);
}
}
}
export default Flicker;
If this isn't the correct way to do this type of changes in what jsx gets rendered on the app, how should it be done?
You have the right idea - but in a class there is only one render method. Your logic does belong inside the render. This should do what you're looking for:
class Flicker extends React.Component {
constructor(props){
super(props);
this.state = {mode: "On"};
}
flipOn(){
this.setState({mode: "On"})
}
flipOff(){
this.setState({mode: "Off"})
}
render () {
return (
<button onClick={(this.state.mode === 'Off') ? this.flipOn : this.flipOff}>State: {this.state.mode}</button>
)
}
}
export default Flicker;
Put the conditional logic inside the render() method.
Something like this...
class Example extends React.Component {
// the rest of your code
render() {
const { mode } = this.state;
return(
<div>
{mode ==="on" && <button onClick={this.flipOn}>State: On</button> }
{mode === "off" && <button onClick={this.flipOff}>State: Off</button>}
</div>
)
}
}
Your component changes the state. The state is boolean - either on (true) or not (false). Since you're state is switched between two value, you can use a single method (toggle). Since we check the previous state, it it's better to setState via updater.
You need to bind the method to this, by using bind in the constructor (method 4) or an arrow function via an instance method (method 5).
Now in the (single) render, you only need to change the text according to the state of on:
class Flicker extends React.Component{
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const text = this.state.on ? 'On' : 'Off';
return (
<button onClick={this.toggle}>State: {text}</button>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>
Real world example
The button should probably get on and toggle via props. Now they are available outside, and the Flicker's only concern is calling toggle when it's clicked, and changing the text according to on:
const Toggler = ({ on, toggle }) => (
<button onClick={toggle}>
State: {on ? 'On' : 'Off'}
</button>
);
class Flicker extends React.Component {
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const { on } = this.state;
return (
<div>
<Toggler toggle={this.toggle} on={on} />
{on &&
'I\'m displayed only when I\'m On'
}
</div>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>
I am creating a react component where I need to show hide a dom element depending on a props. Below you will find a basic idea of the problem. The code is simplified off course.
<UploadManager
show={show}
>
{content}
</UploadManager>
The Component
export default class UploadManager extends React.Component{
openDrawer(){
// change props.open ???
}
render(){
<Drawer
visible={this.props.show}
>
<p>Content</p>
</Drawer>
<Button onClick={this.openDrawer} />
}
}
Explanation
show props of the component UploadManager open/close a Drawer component
The parent component can pass the show props and show/hide the Drawer. I got this part working correctly.
Need help with: The component have a floating button. When Clicked it should open (show) the Drawer. Meaning it should set this.props.show to true.
I tried to maintain a internal state of the component which manages the show/hide property of the drawer but it becomes too buggy because of the props + state involvement.
Your answer is fine except you don't need the openDrawer in the UploadManager component, actually it could be a dummy functional component, here is a possible solution :
const UploadManager = ({show, onClick}) => (
<Drawer visible={show}>
<p>Content</p>
</Drawer>
<Button onClick={onClick} />
);
Then the parent component would be
onClickShowButton = () => {
this.setState({show: true});
}
render(){
return (
<UploadManager
show={this.state.show}
onClick={this.onClickShowButton}
>
{content}
</UploadManager>
);
}
Need to pass callback from the parent to change the prop. Below is the solution to this problem.
this.onClickShowButton() is a method of the parent.
Parent Component
onClickShowButton = () => {
this.setState({show: true});
}
render(){
return <UploadManager
show={this.state.show}
onClickShowButton={this.onClickShowButton}
>
{content}
</UploadManager>
}
On the component
Checks if the callback is a function, then executes it. Does the job perfectly!
export default class UploadManager extends React.Component{
openDrawer = () => {
if (typeof(this.props.onClickShowButton) === 'function') {
this.props['onClickShowButton']();
}
}
render(){
<Drawer
visible={this.props.show}
>
<p>Content</p>
</Drawer>
<Button onClick={this.openDrawer} />
}
}
If so it executes it. I don't know how correct this method is. If anyone have a better answer, I am open to it.
if the parent component just sets the initial state of your upload manager to shows or hides the drawer when it first renders then you can do this:
<UploadManager
show={show}
>
{content}
</UploadManager>
export default class UploadManager extends React.Component{
constructor(props){
super(props);
this.state = {
open: props.show
}
}
openDrawer(){
this.setState({open: true});
}
render(){
<Drawer
visible={this.state.open}
>
<p>Content</p>
</Drawer>
<Button onClick={this.openDrawer} />
}
}
in this way the initial state of your component would be the show property that its parent passes and then when the button gets clicked, this state gets updated and the drawer is being shown based on the component state. But if you want to control the show property that the parent is passing and you want to control your drawer with that, you should do this:
<UploadManager
show={show}
onUpdateShowState={()=>{this.setState({show: true})}}
>
{content}
</UploadManager>
export default class UploadManager extends React.Component{
openDrawer(){
this.props.onUpdateShowState()
}
render(){
<Drawer
visible={this.props.show}
>
<p>Content</p>
</Drawer>
<Button onClick={this.openDrawer} />
}
}
in the above code you are controling your drawer with the show property that gets passed to your component and you are updating it by calling another property that updates the parent state with this: this.setState({show: true}) -> so here this referes to your parent component where show has been defined and should be written inside the parent component of the uploadManager
Do something like this:
The Component:
export default class UploadManager extends React.Component {
constructor(props){
super(props);
this.state = {
openDrawer : props.show
}
}
componentDidUpdate(){
this.setState({
openDrawer: this.props.show
})
}
openDrawer = () =>{
this.setState({
openDrawer : !this.state.openDrawer
})
}
render() {
<div>
<Drawer
visible={this.state.openDrawer}
>
<p>Content</p>
</Drawer>
<Button onClick={this.openDrawer} />
</div>
}
}
I'm super new to react but excited about its potential. Still getting to grips with the fundamentals of it all so any explanation would be greatly appreciated.
I'm looking to render an 'About' component as the user clicks a button in the 'Nav' component (with the aim to toggle this later down the line).
I've attempted to do it in the simplest way I can think of, but this is obviously very wrong:
class Nav extends React.Component {
renderAbout() {
return (
<About />
);
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.renderAbout()}>About</h2>
</div>
</div>
</div>
);
}
}
Would this have something to do with updating the 'state' of the About component?
Thanks in advance.
You can use state to define if imported component About has to be rendered or not.
class Nav extends React.Component {
state = {
isAboutVisible: false,
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.setState({ isAboutVisible: true }) }>About</h2>
</div>
</div>
{ this.state.isAboutVisible ? <About /> : null }
</div>
);
}
}
You currently do not have "About" component in actual view, you just render it somewhere out there, in the void!
To properly render a component you have to specify its place in JSX expression. Also, as one of the easiest solutions) you probably want to toggle it. So that translates to something like this:
constructor(props){
super(props)
this.state={toggle:false}
}
renderAbout(toggle) {
if(toggle)
return <About />
else return null;
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.setState({toggle: !toggle})}>About</h2>
</div>
</div>
{this.renderAbout(this.state.toggle)}
</div>
);
}
}
Yes, you have to change state of the component. Changing the state will automatically rerender your component. In your example it should be something like:
class Nav extends React.Component {
state = {
showAbout: false; // initial state
}
renderAbout = () => {
if (!this.state.showAbout) return '';
return (
<About />
);
}
// ES6 priavte method syntax
handleButtonClick = () => {
this.setState({showAbout: true});
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={this.handleBtnClick}>About</h2>
{this.renderAbout()}
</div>
</div>
</div>
);
}
}
You could also consider using for example this package: https://www.npmjs.com/package/react-conditions
Also, remember that there is a rule that each method which listen for an event should start with the "handle" word. Like in may example.
I have a view that contains several components.
class ProfileView extends React.Compnent {
constructor() {
super();
this.state = {
user: {}
}
}
componentWillMount() {
fetchData((res) => {
this.setState({user: res});
})
}
render() {
return (
<div>
<SomeComponent user={this.state.user} />
<AnotherComponent />
</div>
)
}
}
Since this is making an async call and rendering the state as an empty object on the initial redner, it's causing what I think to be a problem?
Inside my inner component I have to write validation, which is ok but feels wrong, that is why I am asking this question, is the validation in the example below good practice or am I making a mistake.
class SomeComponent extends React.Compnent {
render() {
if(typeof this.props.user !== 'undefined' && !$.isEmptyObject(this.props.user)) {
return (
<div>
<SomeComponent user={this.state.user} />
<AnotherComponent />
</div>
)
} else {
return (
<div></div>
)
}
}
}
This is the best I could come up with, it works ok but there is a slight jump in my UI because initially I am just rendering a <div>.
How can I improve my approach, or is this the ideal way to do it?
Your implementation is close to what I would do. I think that the best solution is to initially render a component that indicates to the user that data is being fetched from the server. Once that data comes back, you can update the state of your parent component, which will case the child component to render instead. A potential solution might look something like this:
function renderChildComponent() {
const {user} = this.state;
if (user) {
return <Child user={user} />;
}
return <Loading />;
}
export default class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
user: undefined
};
}
componentDidMount() {
fetchData(response => {
this.setState({user: response});
});
}
render() {
return (
<div>
{renderChildComponent.call(this)}
</div>
);
}
}