How to change / toggle React state? - html

I am trying to toggle react state after the button click. After clicking button Work From Office should change to work From Home and vice versa. But it is not working. What am I dong wrong? I am able to change only once. Can we do with if statement? What is simple way?
** React **
import React, { Component } from 'react';
import './ChangeSchedule.css';
class ChangeSchedule extends Component {
constructor(){
super()
this.state = {
// work:'from office'
workFromOffice:true
}
}
changeMyWorkPlace(){
this.setState({
// work:'from Home'
workFromOffice:!this.state.workFromOffice
})
}
render(){
return(
<div>
<div class="schedule change">
<h3>Emplyoee Name: </h3>
<p>Today Pooja is work {this.state.work}</p>
{/* <button class="chageScheduleBtn " onClick = {()=> this.changeMyWorkPlace()}> Change My Schedule </button> */}
<button class="chageScheduleBtn " onClick = {()=> this.workFromOffice() ?'Home': 'Office'}> Change My Schedule </button>
</div>
</div>
)
}
}
export default ChangeSchedule;

You can use a ternary expression to display content for each state.
For example:
{this.state.workFromOffice ? " from Office" : " from Home"}
Now this button should work as you expect:
<button class="chageScheduleBtn" onClick={()=> this.changeMyWorkPlace()}>
Change My Schedule
</button>
See codesandbox for fully working example

You could do it as below. Just change the status when the click happen. And inside the button, use a ternary expression. Like so:
import { Component } from "react";
import "./ChangeSchedule.css";
class ChangeSchedule extends Component {
constructor() {
super();
this.state = {
// work:'from office'
workFromOffice: true,
};
}
changeMyWorkPlace() {
this.setState({
// work:'from Home'
workFromOffice: !this.state.workFromOffice,
});
}
render() {
return (
<div>
<div class="schedule change">
<h3>Emplyoee Name: </h3>
<p>Today Pooja is work {this.state.work}</p>
<button class="chageScheduleBtn " onClick={() => this.workFromOffice()}>
{this.state.workFromOffice ? "Work From Home" : "Work From Office"}
</button>
</div>
</div>
);
}
}
export default ChangeSchedule;

The answer is in the way you're structuring your state. You can make it really simple by just using one entry of the state - workFromOffice. Then, your click handler should care only about changing that state value to the opposite of what was set before. Example:
onClick={() => this.setState({ workFromOffice: !this.state.workFromOffice })}

When the changeMyWorkPlace function created, it captures your initial state and uses it everytime you run the function so only works once. You should instruct react to use up to date state.
try this way.
changeMyWorkPlace(){
this.setState((previousState) => ({
// work:'from Home'
workFromOffice:!previousState.workFromOffice
}))
}

Related

How can I use the useState function as a toggle method?

import React, { useEffect, useState } from "react";
import './Menu.css'
export default function Menu() {
const [classes, setClasses] = useState('container')
return (
<div>
<p>Click on the Menu Icon to transform it to "X":</p>
<div className={classes} onClick={() => setClasses("container change")}>
<div className="bar1"></div>
<div className="bar2"></div>
<div className="bar3"></div>
</div>
</div>
)
}
It works now because of one of the given solutions, but when I click on the icon again, it doesn't go back to the original state. How can I fix that?
I got this menu example from here: https://www.w3schools.com/howto/howto_css_menu_icon.asp
You might want to use useState hook to dynamically change your element's class name.
import React from 'react'
import './Menu.css'
export default function Menu() {
const [classes, setClasses] = useState('container')
return (
<div>
<p>Click on the Menu Icon to transform it to "X":</p>
<div className={classes} onClick={() => setClasses("container change")}>
<div className="bar1"></div>
<div className="bar2"></div>
<div className="bar3"></div>
</div>
</div>
)
}
Something like this maybe. But I'm sure there are other ways too.
if you want to toggle back and forth;
setClasses(prevClasses => {
if (prevClasses = "container") return "container change"
else { return "container" }
})
YOu can use state to check what is the current state (user clicked or not), and then can plan behavior accordingly:
export default function Menu() {
const [toggle, setToggle] = useState(false);
return (
<div>
<p>Click on the Menu Icon to transform it to "X":</p>
<div
// If toggle is true, set className to 'change' else 'contianer'
className={toggle ? 'change' : 'container'}
// toggle state (toggle) on click
onClick={() => setToggle(prevToggle => !prevToggle)}
>
<div className='bar1'></div>
<div className='bar2'></div>
<div className='bar3'></div>
</div>
</div>
);
}

Modelling Dynamic Forms as React Component

How to model dynamic forms as a React Component?
For example I want to create a form shown in an image below:
How can I model this as a React component?
How can I add dynamicity to that component? For example, clicking on "+ Add" button creates another empty textbox and puts it right below the other already rendered textboxes (as shown in an image below).
Can someone help me with the code for the Form below?
In tags I see redux so I can suggest redux-form. Here you have an example of dynamic forms with redux-form.
The difference is in the fact, that beyond the state of form values, we also need to handle the state of form shape/structure.
If you render the inputs by traversing some state object, that is representing the shape of the form, than new input is just a new entry in this state object. You can easily add or remove input fields on the form by managing that state object. E.g. you can write something like this (pseudo react code):
// inputs state of math and algorithms
const state = { math: [obj1, obj2], algorithms: [obj1, obj2] } // obj ~= {id, value}
// render math inputs
const mathMarkup = state.math.map(obj => <input value={obj.value} onChange={...} />)
// add handler for math field
const addMath = () => setState(prevState => ({ math: [...prevState.math, newObj]}))
Here is the example of such form - codesandbox. It's not 100% as on your screen, but the idea should be understandable. Since there are some unclear requirements on your form, I implemented only first two sections, so you can grasp the idea. And, there are no styles :shrug:
Also, you can extract renderXyz methods to separate components, and improve state shape to meet your needs.
I can help you with a reduced way
import React , {Component} from 'react'
import { connect }from 'react-redux'
class main extends Component{
render(){
return(
<div>
<BaselineMath/>
<Algorithms />
</div>
)
}
}
const mapStateToProps = ({}) => {
return{}
}
export default connect (mapStateToProps,{})(main)
class BaselineMath extends Component{
constructor(props){
super(props);
this.state={rows:[1]}
}
_getRows{
return this.state.rows.map((res,key)=>{
return <input placeholder="etc..."/>
})
}
onClickAdd(){
let rows = this.state.rows
rows.push(1)
this.setState({
rows
})
}
render(){
return(
<div>
<Button onClick={this.onClickAdd.bind(this)}>ADD row</Button>
{this._getRows()}
</div>
)
}
}
export default (BaselineMath)
class Algorithms extends Component{
constructor(props){
super(props);
this.state={rows:[1]}
}
_getRows{
return this.state.rows.map((res,key)=>{
return <input placeholder="etc..."/>
})
}
onClickAdd(){
let rows = this.state.rows
rows.push(1)
this.setState({
rows
})
}
render(){
return(
<div>
<Button onClick={this.onClickAdd.bind(this)}>ADD row</Button>
{this._getRows()}
</div>
)
}
}
export default (Algorithms)
you can do the algorithm with anything you want

Show dialog in React - Material UI

I am learning ReactJS. I would like to display dialog when someone clicks on the icon.
Here is the code:
import React, { Component } from 'react';
import { GridList, GridTile } from 'material-ui/GridList';
import FlatButton from 'material-ui/FlatButton';
import Info from 'material-ui/svg-icons/action/info';
import { fullWhite } from 'material-ui/styles/colors';
import Dialog from 'material-ui/Dialog';
import RaisedButton from 'material-ui/RaisedButton';
(... class stuff, handleClose, handleOpen etc.)
showDialoga() {
const actions = [
<FlatButton
label="Cancel"
primary
onClick={this.handleClose}
/>,
<FlatButton
label="Submit"
primary
keyboardFocused
onClick={this.handleClose}
/>,
];
return (
<div>
<RaisedButton label="Dialog" onClick={this.handleOpen} />
<Dialog
title="Dialog With Actions"
actions={actions}
modal={false}
open={this.state.open}
onRequestClose={this.handleClose}
>
The actions in this window were passed in as an array of React objects.
</Dialog>
</div>
);
}
render() {
console.log(this.props);
return (
<div style={styles.root}>
<GridList
cellHeight={180}
style={styles.gridList}
padding={10}
>
{this.props.movieData.map(tile => (
<GridTile
key={tile.original_image}
title={tile.title}
actionIcon={<FlatButton
icon={<Info color={fullWhite} />}
style={style}
onClick={() => this.showDialoga()}
/>}
>
<img src={tile.original_image} />
</GridTile>
))}
</GridList>
</div>
);
}
}
I am able to pass other function like () => console.log('I am clicked') to onClick although I am not able to pass that showDialoga().
Any idea what is the problem?
I do not believe that's how you are supposed to use dialog.
Instead of passing return of React component on click, try setting the dialog opened state to be true/false. Also do not forget to bind this to the class level if you are using functions to render different components that has event listeners.

Add a class to the HTML <body> tag with React?

I'm making a modal in my React project that requires a class to be added to the body when the modal is open and removed when it is closed.
I could do this the old jQuery way by running some vanilla JavaScript which adds / removes a class, however this doesn't feel like the normal React philosophy.
Should I instead setState on my top level component to say whether the modal is open or closed? Even if I did this, as it's rendered into the div on the page it's still a side-effect to edit the body element, so is there any benefit for this extra wiring?
TL;DR use document.body.classList.add and document.body.classList.remove
I would have two functions that toggle a piece of state to show/hide the modal within your outer component.
Inside these functions I would use the document.body.classList.add and document.body.classList.remove methods to manipulate the body class dependant on the modal's state like below:
openModal = (event) => {
document.body.classList.add('modal-open');
this.setState({ showModal: true });
}
hideModal = (event) => {
document.body.classList.remove('modal-open');
this.setState({ showModal: false });
}
With the new React (16.8) this can be solved with hooks:
import {useEffect} from 'react';
const addBodyClass = className => document.body.classList.add(className);
const removeBodyClass = className => document.body.classList.remove(className);
export default function useBodyClass(className) {
useEffect(
() => {
// Set up
className instanceof Array ? className.map(addBodyClass) : addBodyClass(className);
// Clean up
return () => {
className instanceof Array
? className.map(removeBodyClass)
: removeBodyClass(className);
};
},
[className]
);
}
then, in the component
export const Sidebar = ({position = 'left', children}) => {
useBodyClass(`page--sidebar-${position}`);
return (
<aside className="...">
{children}
</aside>
);
};
Actually you don't need 2 functions for opening and closing, you could use document.body.classList.toggle
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
document.body.classList.toggle('modal-open', isOpen);
},[isOpen])
<button onCLick={()=> setIsOpen(!isOpen)}>Toggle Modal</button>
Like what #brian mentioned, try having a top-level container component that wraps around your other components. (assuming you're not using redux in your app)
In this top-level component:
Add a boolean state (eg. modalOpen) to toggle the CSS class
Add methods (eg. handleOpenModal & handleCloseModal) to modify the boolean state.
Pass the methods created above as props into your <Modal /> component
ReactJS has an official React Modal component, I would just use that: https://github.com/reactjs/react-modal

React: Create a new html element on click

I've used React for a couple of weeks now but I have this simple problem that I can't seem to wrap my head around. It's about creating new html elements.
I would just like to know in general if the way that I went about it, is the "right way" or is there another preferred way to create new html element with a click function.
For some reason this problem took awhile for me to figure out and it still feels a bit strange, that's why I'm asking.
Thanks in advance!
import React, { Component } from 'react';
import './Overview.css';
import Project from './Project';
class Overview extends Component {
constructor() {
super()
this.state = {
itemArray: []
}
}
createProject() {
const item = this.state.itemArray;
item.push(
<div>
<h2>Title</h2>
<p>text</p>
</div>
)
this.setState({itemArray: item})
//console.log(this.state)
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return <div className="box" key={index}>{item}</div>
})}
</div>
</div>
);
}
}
export default Overview;
No, this is not a correct approach. You shouldn't be generating HTML elements like that, nor keep them in state - it is against React to manipulate DOM like that. You won't be able to utilize Virtual DOM is the first thing that I can think of.
What you should do instead is keep all data that is needed for rendering in state and then generate the HTML element from there, for instance
createProject() {
const item = this.state.itemArray;
const title = '';
const text = '';
item.push({ title, text })
this.setState({itemArray: item})
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return (
<div className="box" key={index}>
<div>
<h2>{item.title}</h2>
<p>{item.text}</p>
</div>
</div>
)
})}
</div>
</div>
);
}