Why onClick event doesn't change the props.title in React - javascript

I wanna change the title by clicking the button but it doesn't change, can I have an explanation why is that happens?
import './ExpenseItem.css';
import ExpenseDate from './ExpenseDate';
import Card from './Card';
function ExpenseItem(props){
let title = props.expenseTitle;
function clickedFunc(){
title = "Update!";
}
return(
<Card className='expense-item'>
<ExpenseDate expenseDate={props.expenseDate}></ExpenseDate>
<div className='expense-item__description'>
<h2>{title}</h2>
<div className='expense-item__price'>
₹{props.expenseAmount}
</div>
</div>
<button onClick={clickedFunc}>Click</button>
</Card>
);
}
export default ExpenseItem;

This is not how data is handled with React.
The title should be stored in a state variable (see useState).
Once the data is stored in a state variable, you will have to set it with setState. When setState is called in React, the component holding the state variable re-renders. This will in turn cause your ExpenseItem component to re-render because it is a child component of whatever higher level component passed it props.
In your parent component, you should see something like:
require { useState } from 'react';
const ParentComponent = (props) => {
const [title, setTitle] = useState('Original Title');
...
...
...
return (
<div className="ParentComponent">
<ExpenseItem
title={title}
setTitle={setTitle}
expenseAmount={expenseAmount}
/>
</div>
)
}
Then, in your clickedFunc() function:
function clickedFunc() {
props.setTitle("Update!");
}

Related

how do i pass a the input value of the textfield from some component to another component in reactjs?

I am trying to pass the value of the text area from some component in reactjs to be used in another react component. the component value is stored in the first component in a useState hook so I want to access it in another component and run map() function around it . Is this possible in reactjs ? I don't want to put the whole thing in app.js because that is just plain HTML which I don't want. I want to use reactjs function components instead ?
first component:
import React, { useState, useRef, useEffect } from "react";
function Firstcomp() {
const [quotes, setQuotes] = useState(["hi there", "greetings"]);
const reference = useRef();
function sub(event) {
event.preventDefault();
setQuotes((old) => [reference.current.value, ...old]);
console.log(quotes);
return;
}
return (
<>
<div>
<div>
<div>
<h4>jon snow</h4>
</div>
<form onSubmit={sub}>
<textarea
type="textarea"
ref={reference}
placeholder="Type your tweet..."
/>
<button type="submit">Tweet</button>
</form>
{quotes.map((item) => (
<li key={item}>{item}</li>
))}
{/* we can use card display taking item as prop where it
will do the job of filling the <p> in card entry */}
</div>
</div>
</>
);
}
export default Firstcomp;
second component
import React from "react";
function SecondComp(props) {
return (
<div>
<p>{props.message}</p>
</div>
);
}
export default Secondcomp;
Use a global management state like Recoil, Redux ot Context
import React from 'react';
export const UserContext = React.createContext();
export default function App() {
return (
<UserContext.Provider value="Reed">
<User />
</UserContext.Provider>
)
}
function User() {
const value = React.useContext(UserContext);
return <h1>{value}</h1>;
}
on the exemple above we used useContext hook to provide a global variable "value", even its not declared directly in User component, but you can use it by calling the useContext hook.
in this exemple the return value in the user component is "Reed"

React: How to setState in child component from parent component?

I'm new to React and am attempting to set up a Bootstrap modal to show alert messages.
In my parent App.js file I have an error handler that sends a Modal.js component a prop that triggers the modal to show, eg:
On App.js:
function App() {
const [modalShow, setModalShow] = useState(false);
// Some other handlers
const alertModalHandler = (modalMessage) => {
console.log(modalMessage);
setModalShow(true);
}
return (
// Other components.
<AlertModal modalOpen={modalShow}/>
)
}
And on Modal.js:
import React, { useState } from "react";
import Modal from "react-bootstrap/Modal";
import "bootstrap/dist/css/bootstrap.min.css";
const AlertModal = (props) => {
const [isOpen, setIsOpen] = useState(false);
if (props.modalOpen) {
setIsOpen(true);
}
return (
<Modal show={isOpen}>
<Modal.Header closeButton>Hi</Modal.Header>
<Modal.Body>asdfasdf</Modal.Body>
</Modal>
);
};
export default AlertModal;
However, this doesn't work. I get the error:
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
If I change the Modal component to be a 'dumb' component and use the prop directly, eg:
const AlertModal = (props) => {
return (
<Modal show={props.modalOpen}>
<Modal.Header closeButton>Hi</Modal.Header>
<Modal.Body>asdfasdf</Modal.Body>
</Modal>
);
};
It does work, but I was wanting to change the show/hide state on the Modal.js component level as well, eg have something that handles modal close buttons in there.
I don't understand why is this breaking?
And does this mean I will have to handle the Modal close function at the parent App.js level?
Edit - full app.js contents
import React, { useState } from 'react';
import './App.css';
import 'bootstrap/dist/css/bootstrap.css';
import AddUserForm from './components/addUserForm';
import UserList from './components/userList';
import AlertModal from './components/modal';
function App() {
const [users, setUsers] = useState([]);
const [modalShow, setModalShow] = useState(false);
const addPersonHandler = (nameValue, ageValue) => {
console.log(nameValue, ageValue);
setUsers(prevUsers => {
const updatedUsers = [...prevUsers];
updatedUsers.unshift({ name: nameValue, age: ageValue });
return updatedUsers;
});
};
const alertModalHandler = (modalMessage) => {
console.log(modalMessage);
setModalShow(true);
}
let content = (
<p style={{ textAlign: 'center' }}>No users found. Maybe add one?</p>
);
if (users.length > 0) {
content = (
<UserList items={users} />
);
}
return (
<>
<div className="container">
<div className="row">
<div className="col-md-6 offset-md-3">
<AddUserForm onAddPerson={addPersonHandler} fireAlertModal={alertModalHandler}/>
</div>
</div>
<div className="row">
<div className="col-md-6 offset-md-3">
{content}
</div>
</div>
</div>
<AlertModal modalOpen={modalShow}/>
</>
);
}
export default App;
In your modal.js
you should put
if (props.modalOpen) {
setIsOpen(true);
}
in a useEffect.
React.useEffect(() => {if (props.modalOpen) {
setIsOpen(true);
}}, [props.modalOpen])
You should never call setState just like that. If you do it will run on every render and trigger another render, because you changed the state. You should put the setModalShow together with the if clause in a useEffect. E.g.:
useState(() => {
if (modalOpen) {
setIsOpen(true);
}
}, [modalOpen])
Note that I also restructered modalOpen out of props. That way the useEffect will only run when modalOpen changes.
If you already send a state called modalShow to the AlertModal component there is no reason to use another state which does the same such as isOpen.
Whenever modalShow is changed, it causes a re-render of the AlertModal component since you changed it's state, then inside if the prop is true you set another state, causing another not needed re-render when you set isOpen. Then, on each re-render if props.showModal has not changed (and still is true) you trigger setIsOpen again and again.
If you want control over the modal open/close inside AlertModal I would do as follows:
<AlertModal modalOpen={modalShow} setModalOpen={setModalShow}/>
Pass the set function of the showModal state to the modal component, and there use it as you see fit. For example, in an onClick handler.
modal.js:
import React, { useState } from "react";
import Modal from "react-bootstrap/Modal";
import "bootstrap/dist/css/bootstrap.min.css";
const AlertModal = (props) => {
const onClickHandler = () => {
props.setModalOpen(prevState => !prevState)
}
return (
<Modal show={props.modalOpen}>
<Modal.Header closeButton>Hi</Modal.Header>
<Modal.Body>asdfasdf</Modal.Body>
</Modal>
);
};
export default AlertModal;

Can I update the state of a prop

Im new to React. Can we change the state of a prop? For eg I have 2 pieces of code
App.js
import React, { useState } from 'react'
import Print from '../components/Print/print'
const [text, setText] = useState("Hi");
<Print text = {text} />
print.js
import React, { useState } from 'react'
const Print = (props) => {
return(
<p>props</p>
)
export default Print
Is there a way to change the state of the prop i.e use useState() in print.js to update the state. For eg can we do something like setText(prop) in print.js. If not like this then how would you trigger a state change from print.js for variable tech in App.js ?
You cannot and shouldn't try to change the state or values of the props directly in the child component. For changing props you can pass the function as a prop and then emit the new value to that function. So the function will change your props into the parent component and it will further pass down to its child.
<Print text = {text} changeText = {(newVal) => setText(newVal)}/>
And in your child component call this changeText function with new value.
return (
<>
<p>{props.text}</p>
<div onClick={props.changeText('Hello')}>Change Text</div>
</>
);
FYI, {props} is an array and you can access text by {props.text}
you can try this
Pass text and setText in print.js through props
then use setText to update the state of the components

Trigger a function in another component on click a button from a different component in React

I am trying to trigger a start function in a different componentB when I click the start button in componentA
Note: Both components are neither parent to child components
Component A
import React from "react"
function ComponentA(props) {
return (
<div>
<button>Start</button>
</div>
)
}
export default ComponentA;
Component B
import React from "react";
function ComponentB(props) {
const [isStarted, setStarted] = React.useState(false);
const start = () => setStarted(true);
return <div>{isStarted ? "Starting..." : "Not Starting.."}</div>;
}
export default ComponentB;
One way you could do it is by creating a callback prop on ComponentA, changing the state of the parent component of ComponentA and passing it to ComponentB via a prop and capture that prop change with a useEffect.
Example:
Parent
function Parent(){
const [started, setStarted] = useState(false)
return(
<div>
<ComponentA onClick={() => setStarted(true)}/>
<ComponentB started={started}/>
</div>
)
}
ComponentA
function ComponentA({onClick}){
return(
<div>
<button onClick={() => onClick()}/>
</div>
)
}
ComponentB
function ComponentB({started}) {
const [isStarted, setStarted] = React.useState(started);
useEffect(() => {
setStarted(started)
}, [started])
return <div>{isStarted ? "Starting..." : "Not Starting.."}</div>;
}
Another way would be using useContext:
https://reactjs.org/docs/hooks-reference.html#usecontext
https://reactjs.org/docs/context.html
Honestly, I am a bit lazy to also include an example which is in my opinion worse. Here is an example that uses useContext that might be useful.
https://stackoverflow.com/a/54738889/7491597

How to pass a React Hook to a child component

I want to pass the setter of a React Hook to a Child Component. So that a button in the child component updates the state via setter which is saved in the Parent Component. I tried following setup but I get an error message :
TypeError: setshowOptionPC is not a function
onClick
Is my approach even possible? And if not how could I possibly do that structure using a React Hook.
Below a simplified version of my code:
import React, { useState } from "react";
function ChildComponent({ setshowChildOptionBC, setshowChildOptionPC }) (
<div>
<button
onClick={() => {
setshowChildOptionPC(false);
setshowChildOptionBC(true);
}}
>
BC
</button>
<button
onClick={() => {
setshowChildOptionPC(true);
setshowChildOptionBC(false);
}}
>
PC
</button>
</div>
);
function ParentComponent() {
const [showOptionBC, setshowOptionBC] = useState(true);
const [showOptionPC, setshowOptionPC] = useState(false);
return (
<div>
<ChildComponent
setshowChildOptionBC={setshowOptionBC}
setshowChildOptionPC={setshowOptionPC}
/>
{showOptionBC && <div>BC</div>}
{showOptionPC && <div>PC</div>}
</div>
);
}
export default ParentComponent;
I think you just forgot to destructure props in your child component.
This might help.
function ChildComponent({setshowOptionBC, setshowOptionPC}) {..

Categories