I must create upload files unique component for input type file, for it i use react-dropzone library with drag and drop and use this component 4 time in page. Initial Initially, besides the first component, other components must be disabled, but after loading the file, the next component must change state to disable = {true}.How do I properly implement the logic using useState?
sandbox
UploadFile.js
const UploadFile = ({ visible }) => {
const [active, setActive] = useState(false);
useEffect(() => {
if (!visible) {
setActive(true);
}
}, [visible]);
console.log(visible);
const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({
noClick: true,
noKeyboard: true,
disabled: active
});
const files = acceptedFiles.map((file) => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
return (
<>
<div className="file-input">
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<p>Drag drop some files here</p>
<button type="button" onClick={open} disabled={active}>
Open File Dialog
</button>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</div>
</>
);
};
App.js
export default function App() {
return (
<div className="App">
<div className="upload-file">
<h4>Please select file</h4>
<div>
<p>Images</p>
<UploadFile visible />
<UploadFile visible={false} />
</div>
<div>
<p>Documents</p>
<UploadFile visible={false} />
<UploadFile visible={false} />
</div>
<br />
<button type="submit" disabled={true}>
SEND
</button>
</div>
</div>
);
}
you can use only one state currentStep as reference at your parent.
import React, { useCallback, useState } from "react";
import UploadFile from "./UploadFile";
import "./styles.css";
export default function App() {
const [currentStep, setCurrentStep] = useState(1)
const nextStep = useCallback(() => {setCurrentStep(crr => crr + 1)}, [])
return (
<div className="App">
<div className="upload-file">
<h4>Please select file</h4>
<div>
<p>Images</p>
<UploadFile nextStep={nextStep} currentStep={currentStep} step={1} />
<UploadFile nextStep={nextStep} currentStep={currentStep} step={2} />
</div>
<div>
<p>Documents</p>
<UploadFile nextStep={nextStep} currentStep={currentStep} step={3} />
<UploadFile nextStep={nextStep} currentStep={currentStep} step={4} />
</div>
<br />
<button type="submit" disabled={true}>
SEND
</button>
</div>
</div>
);
}
at Upload File you don't need setState, only compare step with currentStep to check if is enabled. you also need to check step === currentStep once drop is accepted, otherwise if you make multilple changes to the same field, would keep opening other fields.
import React from "react";
import { useDropzone } from "react-dropzone";
const UploadFile = ({ nextStep, step, currentStep }) => {
const onDropAccepted = () => {
if(step === currentStep) nextStep()
}
const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({
noClick: true,
noKeyboard: true,
disabled: step > currentStep,
onDropAccepted
});
const files = acceptedFiles.map((file) => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
return (
<>
<div className="file-input">
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<p>Drag drop some files here</p>
<button type="button" onClick={open} disabled={step > currentStep}>
Open File Dialog
</button>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</div>
</>
);
};
export default UploadFile;
you can use react context to create the relationship between each component, and after the first file drop , you can go to the next index of upload component
UploadFile.js
import React, { useEffect, useState, useContext } from "react";
import { useDropzone } from "react-dropzone";
import { DragContext } from "./App";
const UploadFile = ({ index = 0 }) => {
const { currentIndex, setCurrentIndex } = useContext(DragContext); // use context
const [active, setActive] = useState(false);
useEffect(() => {
if (index !== currentIndex) { // compare to active
setActive(true);
} else {
setActive(false);
}
}, [currentIndex, index]);
console.log(active);
const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({
noClick: true,
noKeyboard: true,
disabled: active,
onDropAccepted: () => {
setCurrentIndex(currentIndex + 1);
}
});
const files = acceptedFiles.map((file) => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
return (
<>
<div className="file-input">
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<p>Drag drop some files here</p>
<button type="button" onClick={open} disabled={active}>
Open File Dialog
</button>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</div>
</>
);
};
export default UploadFile;
App.js
import React, { createContext, useState } from "react";
import UploadFile from "./UploadFile";
import "./styles.css";
export const DragContext = createContext({});
export default function App() {
const [currentIndex, setCurrentIndex] = useState(0); // compare with index
return (
<div className="App">
<DragContext.Provider value={{ currentIndex, setCurrentIndex }}>
<div className="upload-file">
<h4>Please select file</h4>
<div>
<p>Images</p>
<UploadFile index={0} />
<UploadFile index={1} />
</div>
<div>
<p>Documents</p>
<UploadFile index={2} />
<UploadFile index={3} />
</div>
<br />
<button type="submit" disabled={true}>
SEND
</button>
</div>
</DragContext.Provider>
</div>
);
}
Related
How can I create an order of adding items. I should be able to add one after the other item during on click. By default it should display the Textarea first and then blockquote ( see below )
a) When a user click on Text area button, it should add one after the blockquote.
b) Then when the user clicks on Code area button, it should add after Textarea. Could someone please advise ?
CSB link: https://codesandbox.io/s/distracted-worker-26jztf?file=/src/App.js
Something similar >> Expected behaviour: https://jsfiddle.net/nve8qdbu/8/
import "./styles.css";
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useForm } from "react-hook-form";
const blogListData = [
{
id: 1,
heading: "React state",
date: "22-May-2022",
tag: "React",
count: "3"
},
{
id: 2,
heading: "Cypress testing detailss",
date: "22-May-2022",
tag: "Cypress",
count: "5"
}
];
const Admin = () => {
const [createImageTag, setImageTag] = useState("");
const [fields, setFields] = useState([{ value: null }]);
const [createCode, setCreateCode] = useState([{ value: null }]);
const [blogList, setBlogList] = useState([]);
const {
register,
handleSubmit,
formState: { errors },
reset
} = useForm();
useEffect(() => {
setBlogList(blogListData);
}, []);
function handleChangeTextArea(i, event) {
const values = [...fields];
values[i].value = event.target.value;
setFields(values);
}
function handleChangeCode(i, event) {
const codeValues = [...createCode];
codeValues[i].value = event.currentTarget.innerText;
setCreateCode(codeValues);
}
function handleTextAreaAdd() {
const values = [...fields];
values.push({ value: null });
setFields(values);
}
function handleCodeAreaAdd() {
const codeValues = [...createCode];
codeValues.push({ value: null });
setCreateCode(codeValues);
}
function handleImageAreaAdd() {
const image = [...createImageTag];
image.push({ value: null });
setCreateCode(image);
}
function handleRemoveText(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function handleRemoveCode(i) {
const codeValues = [...createCode];
codeValues.splice(i, 1);
setCreateCode(codeValues);
}
const handleLogout = () => {
localStorage.removeItem("loginEmail");
};
return (
<div id="App">
<div className="parent">
<div className="adminSection">
<h1>Create a new blog</h1>
<div className="row">
<div className="logout">
<img
src="/images/logout.png"
alt="Logout"
onClick={handleLogout}
></img>
</div>
<div className="createBlogSection">
<div className="row">
<button
onClick={() => handleTextAreaAdd()}
className="textAreaBtn"
>
Text Area
</button>
<button
onClick={() => handleCodeAreaAdd()}
className="codeAreaBtn"
>
Code Area
</button>
<button
onClick={() => handleImageAreaAdd()}
className="imageAreaBtn"
>
Add Image
</button>
</div>{" "}
<br></br>
<div className="row">
{fields.map((field, idx) => {
return (
<div key={`${field}-${idx}`} className="dtextArea">
<button
type="button"
onClick={() => handleRemoveText(idx)}
className="closeElement"
>
X
</button>
<textarea
type="text"
id="blogtext"
placeholder="Enter your text here"
className="defaultTextArea"
{...register("blogtext", {
required: true,
minLength: {
value: 25,
message: "Minimum length of 25 letters"
}
})}
value={field.value || ""}
onChange={(e) => handleChangeTextArea(idx, e)}
/>
<span className="validationmsg">
{errors.blogtext &&
errors.blogtext.type === "required" && (
<span>Blog text is required !</span>
)}
{errors.blogtext && (
<span>{errors.blogtext.message}</span>
)}
</span>
</div>
);
})}
</div>
<div className="row">
{createCode.map((code, idx) => {
return (
<div key={`${code}-${idx}`} className="dCodeArea">
<button
type="button"
onClick={() => handleRemoveCode(idx)}
className="closeElement"
>
X
</button>
<blockquote
type="text"
id="blogCode"
contentEditable="true"
className="codehighlight"
placeholder="Enter your code here"
{...register("blogCode", {
required: true
})}
value={code.value || ""}
onInput={(e) => handleChangeCode(idx, e)}
/>
</div>
);
})}
</div>
<div className="row">
</div>
<div className="row">
<div className="submitSection">
<input type="submit" className="submitBtn" />
</div>
</div>
</div>
</div>
</div>
<div className="blogListSection">
<h1>Edit blogs</h1>
<div className="row">
<div className="editBlogSection">
{blogList.map(({ id, heading, count }) => (
<a
key={id}
href="https://www.testingsite.com/"
className="blogitems"
>
<pre>
<span>{count}</span> {heading}
</pre>
</a>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default Admin;
react is designed for components . each of your list elements should be refactored by a component.then it would be easier. i think a single react component could do the trick
I want to submit and close with the button "save". change type to "button" type="submit" value="submit" onClick={ onClose } don't submit and don't close. I am using onClose for close the modal
import { useAppDispatch } from '../redux/hooks';
import { userInfo } from '../redux/model';
import { useForm } from 'react-hook-form';
import { ModalProps } from '../redux/model';
import { setList } from '../redux/slice';
type UI = userInfo;
const AddData: React.FC<ModalProps> = ({ isOpen, onClose }) => {
const [ page, setPage ] = useState(0);
const FormTitles = ["Invoice Address", "Bank Data", "Contact"];
const dispatch = useAppDispatch();
const { register, handleSubmit, formState: { errors, isValid }} = useForm<UI>({ mode: "all" });
const onSubmit = handleSubmit((data) => {
dispatch(setList(data));
});
return isOpen ? (<div className="viewModal">
<div className='modalContent'>
<form onSubmit={ onSubmit }>
<div className="contentForm">
<div className="closeX" onClick={ onClose }>x</div>
<div className="labels">
<div className="titleTxt">{ FormTitles[page] }</div>
{ page === 0 && (<>
<div className="labelInput">
<label htmlFor="additional">Additional</label>
<input { ...register("additional")} id="additional" />
</div>
<div className="labelInput">
... // content
</div>
<div className="labelInput">
... // content
</div>
</>)}
{ page === 1 && (<>
<div className="labelInput">
<label htmlFor="iban">IBAN</label>
<div className="dirCol">
<input { ...register("iban", { required: true, maxLength: 30 })} id="iban" />
<div className="required">{ errors.iban && "This is required!" }</div>
</div>
</div>
<div className="labelInput">
... // content
</div>
{ page === 2 && (<>
{/* *** fax *** */}
<div className="labelInput">
<label htmlFor="fax">Fax</label>
<input { ...register("fax")} id="fax" />
</div>
<div className="labelInput">
... // content
</div>
<div className="labelInput">
... // content
</div>
</div>
<div className="labelButton">
<button className="button2" onClick={ onClose }>Cancel</button>
{ page !== 0 && (<div className="button2 textCancel" onClick={() => { setPage((x) => x - 1)}}
>Previous</div>)}
{ page !== 2 && (<button className="button1" type="button" disabled={ !isValid } onClick={() => { setPage((x) => x + 1)}}
>Next</button>)}
{ page === 2 && (<>
<button className="button1" type="submit" value="submit" onClick={ onClose }>Save</button>
</>)}
</div>
</div>
</form>
</div>
</div>) : null
};
export default AddData;
here AddData off and on, onClose working with cancel and x button
import { useState } from 'react';
import AddData from './AddData';
import { useAppDispatch, useAppSelector } from '../redux/hooks';
import { removeList } from '../redux/slice';
const ListTable = () => {
const [ isModalOpen, setIsModalOpen ] = useState(false);
const toogleModal = () =>setIsModalOpen(!isModalOpen);
const dispatch = useAppDispatch();
const selector = useAppSelector((state) => state.users.list );
return <>
<AddData isOpen={ isModalOpen } onClose={ toogleModal }>
</AddData>
<nav>
<button onClick={ toogleModal }>Add</button>
</nav>
<div className='content'>
... // content
</div>
</>
};
export default ListTable;
Inside of the onClick for your save button you can call onSubmit and onClose.
import { useAppDispatch } from "../redux/hooks";
import { userInfo } from "../redux/model";
import { useForm } from "react-hook-form";
import { ModalProps } from "../redux/model";
import { setList } from "../redux/slice";
type UI = userInfo;
const AddData: React.FC<ModalProps> = ({ isOpen, onClose }) => {
const [page, setPage] = useState(0);
const FormTitles = ["Invoice Address", "Bank Data", "Contact"];
const dispatch = useAppDispatch();
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<UI>({ mode: "all" });
const onSubmit = handleSubmit((data) => {
dispatch(setList(data));
});
return isOpen ? (
<div className="viewModal">
<div className="modalContent">
<form onSubmit={onSubmit}>
<div className="contentForm">
<div className="closeX" onClick={onClose}>
x
</div>
<div className="labels">
<div className="titleTxt">{FormTitles[page]}</div>
{page === 0 && (
<>
<div className="labelInput">
<label htmlFor="additional">Additional</label>
<input {...register("additional")} id="additional" />
</div>
<div className="labelInput">{/* ... // content */}</div>
<div className="labelInput">{/* ... // content */}</div>
</>
)}
{page === 1 && (
<>
<div className="labelInput">
<label htmlFor="iban">IBAN</label>
<div className="dirCol">
<input
{...register("iban", { required: true, maxLength: 30 })}
id="iban"
/>
<div className="required">
{errors.iban && "This is required!"}
</div>
</div>
</div>
<div className="labelInput">... // content</div>
</>
)}
{page === 2 && (
<>
{/* *** fax *** */}
<div className="labelInput">
<label htmlFor="fax">Fax</label>
<input {...register("fax")} id="fax" />
</div>
<div className="labelInput">{/* ... // content */}</div>
<div className="labelInput">{/* ... // content */}</div>
</>
)}
</div>
<div className="labelButton">
<button className="button2" onClick={onClose}>
Cancel
</button>
{page !== 0 && (
<div
className="button2 textCancel"
onClick={() => {
setPage((x) => x - 1);
}}
>
Previous
</div>
)}
{page !== 2 && (
<button
className="button1"
type="button"
disabled={!isValid}
onClick={() => {
setPage((x) => x + 1);
}}
>
Next
</button>
)}
{page === 2 && (
<>
<button
className="button1"
type="submit"
value="submit"
onClick={() => {
onSubmit();
onClose;
}}
>
Save
</button>
</>
)}
</div>
</div>
</form>
</div>
</div>
) : null;
};
export default AddData;
I have this toggle react component. I am trying to access handleChangeTogle when i click on the Toggle but call is not reaching handleChangeTogle.
What am i doing wrong here?
const handleChangeTogle = () => {
setdomestic_voilence(!domestic_voilence);
};
<Toggle
checked={domestic_voilence}
text="Is Active"
onChange={() => handleChangeTogle}
offstyle="btn-danger"
onstyle="btn-success"
/>
import React from "react";
function Toggle(props) {
console.log(props);
const {
text,
size = "default",
defaultChecked,
disabled,
onChange,
offstyle = "btn-danger",
onstyle = "btn-success",
} = props;
let displayStyle = defaultChecked ? onstyle : offstyle;
return (
<>
<label>
<span className={` switch-wrapper`}>
<input
type="checkbox"
// checked={defaultChecked}
// onChange={(e) => onChange(e)}
/>
<span className={`${displayStyle} switch`}>
<span className="switch-handle" />
</span>
</span>
{/* <span className="switch-label">gyyghiyg</span> */}
</label>
</>
);
}
export default Toggle;
I tested your problem and solved it this way. (I used typescript with React. You can remove Interface and types).
Toggle.tsx
import React from "react";
import { ToggleInterface } from './interfaces';
const Toggle = ({ text, handleChange, offstyle, onstyle }: ToggleInterface) => {
return (
<>
<label>
<span className={`switch-wrapper`}>
<input
type="checkbox"
onChange={(e) => handleChange(e)} />
<span className="switch">
<span className="switch-handle" />
</span>
</span>
</label>
</>
);
}
export default Toggle;
App.tsx
const handleChangeToggle = () => {
console.log("handleChangeToggle is worked !!");
};
return (
<div className="App">
<Toggle
text="Is Active"
handleChange={() => handleChangeToggle() }
offstyle= "btn-danger"
onstyle= "btn-success"
/>
);
}
export default App;
interface.ts
export interface ToggleInterface {
text: string,
handleChange: Function,
offstyle: string,
onstyle: string
}
How to add the checkbox or radio button inside the map method. I have created question and answer app. I need to add checkbox or radio button for the answers. Below in the card component is where the question and answer is getting printed out. How can i add the radio button in there so user can check the answer.
import React, { useState, useEffect } from "react";
import { Fragment } from "react";
import "./Survey.css";
import CreateSurvey from "../modals/CreateSurvey";
import Card from "../card/Card";
const Survey = () => {
const [modal, setModal] = useState(false);
const [surveyList, setSurveyList] = useState([]);
useEffect(() => {
let arr = localStorage.getItem("surveyList");
if (arr) {
let obj = JSON.parse(arr);
setSurveyList(obj);
}
}, []);
const deleteSurvey = (index) => {
let tempList = surveyList;
tempList.splice(index, 1);
localStorage.setItem("surveyList", JSON.stringify(tempList));
setSurveyList(tempList);
window.location.reload();
};
const toggle = () => {
setModal(!modal);
};
const updateListArray = (obj, index) => {
let tempList = surveyList;
tempList[index] = obj;
localStorage.setItem("surveyList", JSON.stringify(tempList));
setSurveyList(tempList);
window.location.reload();
};
const saveSurvey = (surveyObj) => {
let tempList = surveyList;
tempList.push(surveyObj);
localStorage.setItem("surveyList", JSON.stringify(tempList));
setSurveyList(surveyList);
setModal(false);
};
return (
<Fragment>
<div className="header text-center">
<h5>Survey</h5>
<button className="btn btn-primary" onClick={() => setModal(true)}>
Create Survey
</button>
</div>
<div className="survey-container">
{surveyList &&
surveyList.map((obj, index) => (
<Card
surveyObj={obj}
index={index}
deleteSurvey={deleteSurvey}
updateListArray={updateListArray}
/>
))}
</div>
<CreateSurvey toggle={toggle} modal={modal} save={saveSurvey} />
</Fragment>
);
};
export default Survey;
//Card.js
import React, { useState } from "react";
import "./Card.css";
const Card = ({ surveyObj, deleteSurvey, index }) => {
const [modal, setModal] = useState(false);
const toggle = () => {
setModal(!modal);
};
const deleteHandler = () => {
deleteSurvey(index);
};
return (
<div>
<div className="card-wrapper mr-5">
<div className="card-top"></div>
<div className="survey-holder">
<span className="card-header">{surveyObj.name}</span>
<p className="answer"> {surveyObj.answerOne}</p>
<div className="icons">
<i className="far fa-edit edit"></i>
<i className="fas fa-trash-alt delete" onClick={deleteHandler}></i>
</div>
</div>
</div>
</div>
);
};
export default Card;
//Createsurvey.js
import React, { useState } from "react";
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
import { Fragment } from "react";
const CreateSurvey = ({ modal, toggle, save }) => {
const [question, setQuestion] = useState("");
const [answerOne, setAnswerOne] = useState("");
const [answerTwo, setAnswerTwo] = useState("");
const [answerThree, setAnswerThree] = useState("");
const [answerFour, setAnswerFour] = useState("");
const changeHandler = (e) => {
const { name, value } = e.target;
if (name === "question") {
setQuestion(value);
} else {
setAnswerOne(value);
}
};
const saveHandler = (e) => {
e.preventDefault();
let surveyObj = {};
surveyObj["name"] = question;
surveyObj["answerOne"] = answerOne;
surveyObj["answerTwo"] = answerTwo;
surveyObj["answerThree"] = answerThree;
surveyObj["answerFour"] = answerFour;
save(surveyObj);
};
return (
<Fragment>
<Modal isOpen={modal} toggle={toggle}>
<ModalHeader toggle={toggle}>Create a Survey Question</ModalHeader>
<ModalBody>
<form>
<div>
<div className="form-group">
<label>Survey Questions</label>
<input
type="text"
className="form-control"
value={question}
name="question"
onChange={changeHandler}
/>
</div>
</div>
<div className="mt-2">
<label>Survey Answers</label>
<div className="form-group">
<label>Answer 1</label>
<input
type="text"
className="form-control mt-2 mb-2"
value={answerOne}
name="answerOne"
onChange={changeHandler}
/>
</div>
<div className="form-group">
<label>Answer 2</label>
<input
type="text"
className="form-control mt-2 mb-2"
value={answerTwo}
name="answerTwo"
onChange={changeHandler}
/>
</div>
<div className="form-group">
<label>Answer 3</label>
<input
type="text"
className="form-control mt-2 mb-2"
value={answerThree}
name="answerThree"
onChange={changeHandler}
/>
</div>
<div className="form-group">
<label>Answer 4</label>
<input
type="text"
className="form-control mt-2 mb-2"
value={answerFour}
name="answerFour"
onChange={changeHandler}
/>
</div>
</div>
</form>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={saveHandler}>
Create
</Button>
<Button color="secondary" onClick={toggle}>
Cancel
</Button>
</ModalFooter>
</Modal>
</Fragment>
);
};
export default CreateSurvey;
What I am understanding is that you want to add multiple component in map method. You can simply do it as:
{surveyList &&
surveyList.map((obj, index) => (
<>
<Card
surveyObj={obj}
index={index}
deleteSurvey={deleteSurvey}
updateListArray={updateListArray}
/>
<input type="checkbox" name="userchoice" />
</>
))}
Updated CodeSandbox link here
The Add transaction button is not rendering the transaction list in transaction history container
This piece of code {transaction && transaction.map(trans).... is rendering the app UI but the Add transaction button is not generating the Transaction component dynamically in transaction history container}
import React from 'react';
const AddTransaction =
({item,amount,setItem,setAmount,transaction,setTransaction})
=> {
const onSubmit = (e) => {
e.preventDefault();
setTransaction([...transaction,
{
text: item,
amount: amount,
id: Math.floor(Math.random()*1000),
}
] );
setItem('');
setAmount('');
}
return (
<div className='addtransaction-container'>
<div className='add-trans-header'>
<h4>Add New Transaction</h4>
</div>
<form>
<div className="form-control">
<label htmlFor="text">Text</label>
<input type="text" value={item}
onChange={(e) => setItem(e.target.value)}
placeholder="Enter text..." />
</div>
<div className="form-control">
<label htmlFor="amount"
>Amount <br />
(negative - expense, positive - income)
</label>
<input type="number" value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Enter amount..." />
</div>
<button type='button' onClick={onSubmit}
value='submit'
className="btn">
Add transaction
</button>
</form>
</div>
);
}
export default AddTransaction;
The map function is not rendering the Transaction
component in TransactionList.js file
import React from 'react'
import './App.css';
import Transaction from './Transaction.js';
const TransactionList = ({text,transaction,amount}) => {
return (
<div className='transactionlist-container'>
<div className='transactionlist-header-container'>
<h4>
Transaction History
</h4>
</div>
<ul>
<li>
{ transaction.map(trans =>
<Transaction
amount={transaction.amount}
text={transaction.text}
key={transaction.id} />
)}
</li>
</ul>
</div>
)
}
export default TransactionList;
My Transaction.js file have a ul list with the input text and amount but the component is not rendering in the app UI.
import React from 'react'
const Transaction = ({transaction,text,amount}) => {
return (
<div className='transaction'>
{text}<span>{amount}</span>
</div>
)
}
export default Transaction;
I have recreated the app, which is working without any issue.
Here is the link to the working demo: StackBlitz
import React, { useState, useEffect } from "react";
import TransactionList from "./TransactionList";
import AddTransaction from "./AddTransaction";
const App = () => {
const [transaction, setTransaction] = useState([]);
const handleTransaction = value => {
setTransaction([...transaction, value]);
};
const expenseList = transaction.filter(trans => Number(trans.amount) < 0);
const expense = expenseList.reduce(
(acc, curr) => acc + Number(curr.amount),
0
);
const amountList = transaction.filter(trans => Number(trans.amount) > 0);
const amount = amountList.reduce((acc, curr) => acc + Number(curr.amount), 0);
useEffect(() => {
console.log("From app:", transaction);
}, [transaction]);
return (
<div className="transactionlist-container">
<div>
<span>income: {JSON.stringify(amount)}</span>{" "}
<span> total expense: {JSON.stringify(expense)}</span>
<span> balance: {amount + expense}</span>
</div>
<TransactionList transaction={transaction} />
<AddTransaction
transaction={transaction}
handleTransaction={handleTransaction}
/>
</div>
);
};
export default App;
import React from "react";
import Transaction from "./Transaction";
const TransactionList = ({ transaction }) => {
console.log("from tl:", transaction);
return (
<div className="transactionlist-container">
<div className="transactionlist-header-container">
<h4>Transaction History</h4>
</div>
{transaction.map(trans => (
<Transaction amount={trans.amount} text={trans.text} key={trans.id} />
))}
</div>
);
};
export default TransactionList;
import React from "react";
const Transaction = ({ text, amount }) => {
return (
<div className="transaction">
{text}
<span>{amount}</span>
</div>
);
};
export default Transaction;
import React,{useState} from "react"
const AddTransaction =
({handleTransaction})
=> {
const [item,setItem] = useState("")
const [amount, setAmount] = useState(0)
const onSubmit = (e) => {
e.preventDefault();
handleTransaction(
{
text: item,
amount: amount,
id: Math.floor(Math.random()*1000),
}
);
setItem('');
setAmount('');
}
return (
<div
className="inputBox"
>
<div className='add-trans-header'>
<h4>Add New Transaction</h4>
</div>
<form>
<div className="form-control">
<label htmlFor="text">Text</label>
<input type="text" value={item}
onChange={(e) => setItem(e.target.value)}
placeholder="Enter text..." />
</div>
<div className="form-control">
<label htmlFor="amount"
>Amount <br />
(negative - expense, positive - income)
</label>
<input type="number" value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Enter amount..." />
</div>
<button type='button' onClick={onSubmit}
value='submit'
className="btn">
Add transaction
</button>
</form>
</div>
);
}
export default AddTransaction;