React: How to update a variable that depends on a state changes - reactjs

I'm trying to use React to make a Dungeons and Dragons Character Sheet (click here for a preview). One component I am stuck on is the ability modifier, shown below:
With React.useState(), I can automatically changes the number "7" by clicking the arrows
I've cracked the basics of useState(). I can now change the variable titled count in the red circle if I click the MUI arrow buttons. I want the string value (now set to "15") to be dynamic, and equal to (count-10)/2.
I am trying to use onChange to make it work, but no luck. My code for the component is shown below. What am I doing wrong?
`
import React from "react"
import ArrowBackIosIcon from '#mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '#mui/icons-material/ArrowForwardIos';
import Button from '#mui/material/Button';
export default function StatBox(props) {
const[count, setCount] = React.useState(8)
const incrementCount = () => {
setCount(count+1);
}
let modifier = "15"
if (count && !isNaN){
const modifierNum = Math.floor(Number(count)-10/2)
if(modifierNum > 0){
modifier = "+" + modifierNum
}
else{
modifier = modifierNum.toString()
}
}
const decrementCount = () => {
setCount(count-1);
}
return (
<div className="col text-center border border-danger rounded mt-2 mb-2">
<label>{props.stats}</label>
<div className="d-and-d-statbox-modifier">{modifier}</div>
<div className="row justify-content-center mb-2">
<Button
onClick={decrementCount}
onChange={(e) => count.onChange(count, e.target.value)}
size="sm"
variant="contained"
color="error"
>
<ArrowBackIosIcon fontSize="small" />
</Button>
<div>
<span className="mx-1 border-danger text-center dndstatrow">
{count}
</span>
</div>
<Button
onClick={incrementCount}
onChange={(e) => count.onChange(count, e.target.value)}
size="sm"
variant="contained"
color="error"
>
<ArrowForwardIosIcon fontSize="small" />
</Button>
</div>
</div>
);
}
`

Solved!
import React from "react"
import ArrowBackIosIcon from '#mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '#mui/icons-material/ArrowForwardIos';
import Button from '#mui/material/Button';
export default function StatBox(props) {
const[count, setCount] = React.useState(8)
const incrementCount = () => {
setCount(count+1);
}
let modifierNum = Math.floor(((count)-10)/2)
let modifier = ""
if(modifierNum > 0){
modifier = "+" + modifierNum
}
else{
modifier = modifierNum.toString()
}
const decrementCount = () => {
setCount(count-1);
}
return (
<div className="col text-center border border-danger rounded mt-2 mb-2">
<label>{props.stats}</label>
<div className="d-and-d-statbox-modifier">{modifier}</div>
<div className="row justify-content-center mb-2">
<Button
onClick={decrementCount}
size="sm"
variant="contained"
color="error"
>
<ArrowBackIosIcon fontSize="small" />
</Button>
<div>
<span className="mx-1 border-danger text-center dndstatrow">
{count}
</span>
</div>
<Button
onClick={incrementCount}
size="sm"
variant="contained"
color="error"
>
<ArrowForwardIosIcon fontSize="small" />
</Button>
</div>
</div>
);
}

Related

adding specefic data from in array without loop

i want get all the data from dataFromes and put it in Modal.title but what i did using map it only show the first in array of dataFromes
import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import Form from 'react-bootstrap/Form';
import { useSelector } from 'react-redux';
function ViewPop(props) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const dataFromes = useSelector(state => state.data.value);
const expenseForms = useSelector(state => state.expend.value);
return (
<div>
<Button variant= {props.outline} onClick={() => {
handleShow();
}} className= "mx-1">
{props.text}
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<div className='title flex'>
{dataFromes.map((data, index) => (
<Modal.Title>Expenses - {data.text}</Modal.Title>
))}
<Button variant="outline-danger" className='ml-2'>Delete</Button>
</div>
</Modal.Header>
<Form className="mx-3 mt-3">
<Form.Group className="mb-3">
{expenseForms ? expenseForms.map(expense => (
<div key={expense.text} className='expense flex justify-between items-center mb-3'>
<span className='fs-4'>{expense.text}</span>
<span className='fs-5'><span>$</span>{expense.amount} <Button className= "text-center mb-[9px] w-7 ml-1" size="sm" variant="outline-danger"><span className="text-[10px]">X</span></Button></span>
</div>
)) : null}
</Form.Group>
</Form>
{/* <Modal.Title>Expenses - {data.text}</Modal.Title> */}
</Modal>
</div>
)
};
export default ViewPop;
hello i want get all the data from dataFromes and put it in Modal.title but what i did using map it only show the first in array of dataFromes

Set unique key for useState with multiple inputs

I have an App with 2 inputs where you can choose 1 muscle and a number of exercices for this muscle.
I added an "Add" button to duplicates these inputs to add a new muscle to work with its number of exercices.
The problem is, when I change an input, all same inputs will change because they have the same key (I guess).
Anyone can show me how to solve this ?
Here is my code :
import { Formik, Form, Field } from "formik";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { getAllMuscles, numberOfExercices } from "../helpers/dataModuler";
import { setInLocalStorage } from "../helpers/localStorageHandler";
export const ProgramForm: React.FC<{}> = () => {
const [numberOfMusclesInProgram, setNumberOfMusclesInProgram] = useState(1);
const navigate = useNavigate();
const listOfMuscles = getAllMuscles();
const initialValues = {
numberOfExercices: 0,
muscle: listOfMuscles[0]
};
const handleSubmit = (values: {}) => {
setInLocalStorage("program", values);
navigate("/programme");
};
return (
<>
<Formik
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<div id="container">
{[...Array(numberOfMusclesInProgram)].map((n, i) => (
<DisplayMuscleInProgram key={n} />
))}
</div>
<button
onClick={() =>
setNumberOfMusclesInProgram(numberOfMusclesInProgram + 1)
}
type="button"
className="rounded-md border"
>
Add
</button>
<div className="text-center">
<button
type="submit"
className="mt-8 rounded-lg bg-primary p-2 text-white"
>
Let's gooooo 💪
</button>
</div>
</Form>
</Formik>
</>
);
};
export const DisplayMuscleInProgram = (props: { key: any }) => {
const listOfMuscles = getAllMuscles();
return (
<>
<div className="my-4 grid grid-cols-5 justify-between gap-5">
<Field
as="select"
className="col-span-3 rounded-lg bg-lightGray p-3"
name={`muscle-${props.key}`}
>
{listOfMuscles.map((muscle, key) => (
<option key={key}>{muscle}</option>
))}
</Field>
<Field
as="select"
className="col-span-2 rounded-lg bg-lightGray p-3"
name="numberOfExercices"
>
{numberOfExercices(10)}
</Field>
</div>
</>
);
};
In ProgramForm replace <div id="container" /> with the following (replacing key={n} with key={i}):
<div id="container">
{[...Array(numberOfMusclesInProgram)].map((n, i) => (
<DisplayMuscleInProgram key={i} />
))}
</div>
Alternatively, you may try:
<div id="container">
{[...Array(numberOfMusclesInProgram).keys()].map((n) => (
<DisplayMuscleInProgram key={n} />
))}
</div>
Source: https://stackoverflow.com/a/33352604/975164
Currently, all your inputs have exactly the same name because of the following lines in DisplayMuscleInProgram:
<Field
as="select"
className="col-span-3 rounded-lg bg-lightGray p-3"
name={`muscle-${props.key}`} // this line
>
React docs recommend using someEntity.id as keys and warn against using indexes as keys when the order of list items is expected to change. However, in this case, I believe you will be safe.
As a side note: Since you are using Formik, I recommend using Formik's built-in <FieldArray /> for this purpose i.e. add more inputs to the form on button click. You may read more about it here: https://formik.org/docs/api/fieldarray

Dynamic add Image and data with Ant-Design and ReactJs

I can't add an image while executing. When adding an image, the image does not appear, I use a dynamic form with the same inputs when adding a form which will later be stored by looping, but I have problems when adding images. I use a plugin from ant design.
Here's the code.
import React,{useState} from 'react'
import { Button,Form,Input } from 'antd';
import {
PlusOutlined,
MinusCircleOutlined,
} from '#ant-design/icons';
function Ad_abhb() {
const [Img1,setImg] = useState([]);
const i = 0;
const AddImg4 = (i) =>{
const [imgPrev1,setImgPrev1] = useState('');
const ImgC1=(e)=>{
const reader = new FileReader();
const vImg = e.target.files[0];
setImgPrev1(URL.createObjectURL(vImg));
reader.readAsDataURL(vImg);
reader.onload = () => {[...setImg(reader.result)]}
}
return(<>
<Form.Item className='imgHobi'>
<Input id={'uImg4'+i.kd} type="file" accept=".jpg,.jpeg,.png" onChange={ImgC1} hidden />
<label htmlFor={'uImg4'+i.kd}>
{imgPrev1.length >= 1 ?<>
<div className='imgLoad'>
<img src={imgPrev1} className='getImg' alt=''/>
</div>
</>:<>
<div className='imgLoad'>
<div className='UpImg'>
<div className='UpCore'>
<PlusOutlined style={{fontSize:'20px'}}/>
</div>
</div>
</div>
</>}
</label>
</Form.Item>
</>)
}
const SimpanA4=()=>{
console.log(Img1)
}
return (<>
<h1>hobi</h1>
<div className='konten'>
<Form >
<Form.List name='Hobi'>
{(fields, { add, remove }) => (<>
{fields.map((field,i)=>{
return(
<div key={field.key} className='cardAdminMod hobiList' >
<AddImg4 kd={i}/>
<Form.Item className='inputHobi' name="Hobi" {...field}>
<Input />
</Form.Item>
{fields.length > 1 ? <div className='rmHobi'><MinusCircleOutlined onClick={() => remove(field.name)} /></div>: null}
</div>)
})}
<Form.Item className='addPlusHobi'>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Add Data
</Button>
</Form.Item>
</>)}
</Form.List>
</Form>
</div>
<div className="titleAdmin butSide">
<Button onClick={SimpanA4}>Save</Button>
</div>
</>)
}
export default Ad_abhb;
So, can anyone help me?

How to show the content and hide the content conditionally in react using hooks

I am working on a React project that I have to show content and hide the content conditionally when I click the button. For example, I have four buttons, First Button is Frontend, Second Button is Middleware, Third Button is Database, and Fourth Button is Apps.
By Default when I landed on the Home Page Frontend Button should be Highlighted remaining button should be normal. At that time I have to show only Frontend-related frameworks or libraries.
Now when I click Middleware Button then the Middleware Button should be Highlighted At that time I have to show Middleware Frameworks like Node Express etc.
Now when I click Database Button then the Database Button should be Highlighted At that time I have to show Database like Mongo Db, Casandra.
Now when I click Apps Button then the App Button should be Highlighted At that time I have to show Apps like React native, Flutter.
Please help me to achieve this task
This is Home.js
import React, { useState } from 'react';
import './Home.css'
const Home = () => {
return (
<div className='container'>
<div className='row'>
<div className='col-3'>
<button className='btn btn-primary mt-3'>Frontend</button>
</div>
<div className='col-3'>
<button className='btn btn-danger mt-3'>Middleware</button>
</div>
<div className='col-3'>
<button className='btn btn-secondary mt-3'>Database</button>
</div>
<div className='col-3'>
<button className='btn btn-info mt-3'>Apps</button>
</div>
</div>
<div className='row mt-3'>
<div className='col-3'>
<h3>React</h3>
</div>
<div className='col-3'>
<h3>Angular</h3>
</div>
<div className='col-3'>
<h3>Vue</h3>
</div>
<div className='col-3'>
<h3>Ember</h3>
</div>
</div>
</div>
)
}
export default Home
const Home = () => {
const [selected, setSelected] = useState('frontend')
const frontends = ['React', 'Angular', 'Vue']
const middlewares = ['Node', 'Express', 'Hapi']
const databases = ['MongoDB', 'MySQL', 'Casandra']
const apps = ['React Native', 'Flutter']
let showingArr = []
if (selected === 'frontend') {
showingArr = frontends
} else if (selected === 'middleware') {
showingArr = middlewares
} else if (selected === 'database') {
showingArr = databases
} else if (selected === 'apps') {
showingArr = apps
}
return (
<div className='container'>
<div className='row'>
<div className='col-3'>
<button
className='btn btn-primary mt-3'
onClick={() => setSelected('frontend')}
>Frontend</button>
</div>
<div className='col-3'>
<button
className='btn btn-danger mt-3'
onClick={() => setSelected('middleware')}
>Middleware</button>
</div>
<div className='col-3'>
<button
className='btn btn-secondary mt-3'
onClick={() => setSelected('database')}
>Database</button>
</div>
<div className='col-3'>
<button
className='btn btn-info mt-3'
onClick={() => setSelected('apps')}
>Apps</button>
</div>
</div>
<div className='row mt-3'>
{
showingArr.map(item => (
<div className='col-3'>
<h3>{item}</h3>
</div>
))
}
</div>
</div>
)
}
const [show, setShow] = useState(false);
return(
{show ? <content to show when state is true /> : null}
)
here is a more scale able way of doing this . in the future if you want to add new topics of different types for example Pythonwhich can be of type AI , you want have to add an other condition check you can just add your AI toggle button and set onClick as toggleListType('AI')
import React, { useState ,useEffect} from 'react';
import './Home.css'
const Home = () => {
const [listOFtopics,setlistOFtopics]=useState([
{type:'FRONTEND',title:'react'},
{type:'FRONTEND',title:'angular'},
{type:'MIDDLEWEAR',title:'node'},
{type:'MIDDLEWEAR',title:'express'},
])
const [listOFtopicsToDisplay,setlistOFtopicsToDisplay]=useState([])
useEffect(()=>{
//initializing listOFtopicsToDisplay to show FRONEND related topics
setlistOFtopicsToDisplay(listOFtopics.filter(t=> t.type =="FRONTEND"))
},[])
const toggleListType=(type)=>{
setlistOFtopicsToDisplay(listOFtopics.filter(t=> t.type ==type))
}
return (
<div className='container'>
<div className='row'>
<div className='col-6'>
<button onClick={e=>toggleListType('FRONTEND')} className='btn btn-primary mt-3'>Frontend</button>
</div>
<div className='col-6'>
<button onClick={e=>toggleListType('MIDDLEWEAR')} className='btn btn-danger mt-3'>Middleware</button>
</div>
</div>
<div className='row mt-3'>
{
listOFtopicsToDisplay.map(t=><div className='col-3'><h3>Vue</h3</div>)
}
</div>
</div>
)
}
export default Home

React Modal Submit Form

I'm trying to print out a simple buttton clicked text whenever the submit button is click on my reactstrap modal and somehow my code doesn't do anything, not sure what I have wrong.
I have attached a picture for better visualisation , I'm using reactstrap Modal.
import React, { useState } from "react";
import { Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
import Button from "./Button";
// NOTICE
// Modal is brought in with it's own trigger, so import the component where you want the trigger to be.
const ModalComponent = (props) => {
const {
buttonText,
title,
actionButtonText,
cancelButtonText,
children,
className,
} = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const alertshow = () => {
alert("button clicked");
};
const closeBtn = (
<button className="close" onClick={toggle}>
×
</button>
);
return (
<div>
<a className="btn_link" onClick={toggle}>
{buttonText}
</a>
<form onSubmit={alertshow}>
<Modal isOpen={modal} toggle={toggle} className={className}>
<ModalHeader className=" border-0" toggle={toggle} close={closeBtn}>
{title}
</ModalHeader>
<ModalBody className="text-left border-0">
<p className="modal-label">Please enter your email address</p>
{children}
</ModalBody>
<ModalFooter className="modal-footer border-0">
<Button className="btn_secondary modal-btn" onClick={toggle}>
{cancelButtonText}
</Button>{" "}
<input
className="btn btn_primary modal-btn"
type="submit"
value={actionButtonText}
/>
</ModalFooter>
</Modal>
</form>
</div>
);
};
export default ModalComponent;
Its happening form should be part of modal not modal should be part of form. This is why its not referencing onSubmit. You need to do this:
<Modal isOpen={modal} toggle={toggle} className={className}>
<form onSubmit={alertshow}>
...rest all content
</Form>
</Modal>
Here is full code:
import React, { useState } from "react";
import "./styles.css";
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from "reactstrap";
// NOTICE
// Modal is brought in with it's own trigger, so import the component where you want the trigger to be.
const ModalComponent = (props) => {
const {
buttonText,
title,
actionButtonText,
cancelButtonText,
children,
className
} = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const alertshow = () => {
alert("button clicked");
};
const closeBtn = (
<button className="close" onClick={toggle}>
×
</button>
);
return (
<div>
<div onClick={toggle}>{buttonText}</div>
<Modal isOpen={modal} toggle={toggle} className={className}>
<form onSubmit={alertshow}>
<ModalHeader className=" border-0" toggle={toggle} close={closeBtn}>
{title}
</ModalHeader>
<ModalBody className="text-left border-0">
<p className="modal-label">Please enter your email address</p>
{children}
</ModalBody>
<ModalFooter className="modal-footer border-0">
<Button className="btn_secondary modal-btn" onClick={toggle}>
{cancelButtonText}
</Button>{" "}
<input
className="btn btn_primary modal-btn"
type="submit"
value={actionButtonText}
/>
</ModalFooter>
</form>
</Modal>
</div>
);
};
export default function App() {
return (
<div className="App">
<ModalComponent
title="Hello"
cancelButtonText="Cancel"
actionButtonText="Submit"
buttonText="testing"
/>
</div>
);
}
Here is the demo: https://codesandbox.io/s/fervent-bash-51lxe?file=/src/App.js:0-1826
Accepted answer doesn't work when Modal is scrollable.
Here is how to resolve the issue:
<Modal show={ show } onHide={ onClose }
scrollable={ true }
onSubmit={ handleSubmit(onSave) }
dialogAs={ FormWrappedModal }>
<Modal.Header closeButton>
<Modal.Title>some title</Modal.Title>
</Modal.Header>
<Modal.Body>some body</Modal.Body>
<Modal.Footer>some body</Modal.Footer>
</Modal>
We need to introduce custom component FormWrappedModal for that purpose:
const FormWrappedModal = ( props: any)=>{
return (
<form onSubmit={ props.onSubmit }>
<Modal.Dialog { ...props } />
</form>
);
};

Resources