I have a parent component and a child component.
The parent component sends data to the child.
The child modifies the value and the parent should see the change reflected.
But nevertheless there is something that I am not doing well because I do not understand the mechanisms of react well.
Parent component
function ParentComponent() {
var userName = "Didi";
return (
<div className="Parent">
<label>Parent - {userName}</label>
<ChildComponent userName={userName} />
</div>
);
}
Child component
function ChildComponent({ userName }) {
const handleChange = (e) => {
userName = e.target.value;
};
return (
<div className="ChildComponent">
<input type="text" defaultValue={userName} onChange={handleChange} />
<br />
<label>ChildComponent - {userName}</label>
</div>
);
}
React can't monitor free variables for changes. You need to store data in a state (or use some external system like Redux).
If you want to change the state from a different component, then you need to pass the function which changes it to the component.
function ParentComponent() {
const [userName, setUserName] = React.useState("Didi");
return (
<div className="Parent">
<label>Parent - {userName}</label>
<ChildComponent userName={userName} setUserName={setUserName} />
</div>
);
}
function ChildComponent({ userName, setUserName }) {
const handleChange = (e) => {
setUserName(e.target.value);
};
return (
<div className="ChildComponent">
<input type="text" defaultValue={userName} onChange={handleChange} />
<br />
<label>ChildComponent - {userName}</label>
</div>
);
}
Try this:
Parent component
function ParentComponent() {
const [username, setUsername] = useState("");
return (
<div className="Parent">
<label>Parent - {username}</label>
<ChildComponent
username={username}
changeUsername={value => setUsername(value)}
/>
</div>
);
}
Child component
function ChildComponent({ username, changeUsername }) {
const handleChange = e => {
changeUsername(e.target.value);
};
return (
<div className="ChildComponent">
<input type="text" value={username} onChange={handleChange} />
<br />
<label>ChildComponent - {username}</label>
</div>
);
}
You have to do this:
Parent component
import {useState} from 'react';
function ParentComponent() {
const [userName,setUserName]=useState("Didi");
return (
<div className="Parent">
<label>Parent - {userName}</label>
<ChildComponent userName={userName} setUserName={setUserName}/>
</div>
);
}
Child component
function ChildComponent({ userName,setUserName }) {
return (
<div className="ChildComponent">
<input type="text" defaultValue={userName} onChange={(e)=>setUserName(e.target.value)} />
<br />
<label>ChildComponent - {userName}</label>
</div>
);
}
Related
What I need is to be able to customize textAreaCount and other props in each separate instance of <Texarea/>. The textAreaCount is different for each <Texarea/> so how do I modify the component to be able to pass in custom textAreaCount for each <Texarea/>?
https://codesandbox.io/s/rkv88-forked-vjo0rn?file=/src/App.js:0-948
import React, { useState } from "react";
const Textarea = (value, id, maxLength, textAreaLimit) => {
const [textAreaCount, ChangeTextAreaCount] = React.useState(0);
const [state, setstate] = useState({
headline: "",
title: ""
});
const { headline, title } = state;
const changevalue = (e) => {
setstate({
...state,
[e.target.name]: value
});
ChangeTextAreaCount(e.target.value.length);
};
return (
<>
<p>{textAreaCount}/{textAreaLimit}</p>
<textarea
type="text"
rows={5}
id={id}
value={value}
maxLength={maxLength}
onChange={(e) => {
changevalue(e);
}}
/>
</>
);
};
export default function FullWidthTabs() {
return (
<div>
<Textarea value={headline} id="test" maxLength={5} textAreaLimit={5}/>
<Textarea value={title} id="test2" maxLength={10} textAreaLimit={10}/>
</div>
);
}
Forward the props you need.
const Textarea = (props) => {
const [textAreaCount, setTextAreaCount] = React.useState(0);
const recalculate = (e) => {
setTextAreaCount(e.target.value.length);
};
return (
<>
<p>{textAreaCount}/5</p>
<textarea type="text" rows={5} maxLength={5} onChange={recalculate} {...props} />
</>
);
};
Now it will forward any props into the textarea element. This will set the id and will overwrite the rows prop.
<Textarea id="textarea-1" rows={4} />
<Textarea id="textarea-2" rows={5} maxLength={10} />
As we can see you try to pass props as below:
<Textarea value={title} id="test2" maxLength={10} textAreaLimit={10}/>
But In Your Textarea Component you received props argument as multiple args as below:
const Textarea = (value, id, maxLength, textAreaLimit) => {
return (
<>
</>
);
};
Instead that you need to destruct your props argument or you can set whole passed value props as single object props as below:
Method 1:
const Textarea = ({value, id, maxLength, textAreaLimit}) => {
return (
<>
<textarea type="text" id={id} value={value} rows={5} maxLength={maxLength} onChange={recalculate} textAreaLimit={textAreaLimit} />
</>
);
};
Method 2:
const Textarea = ({...props}) => {
return (
<>
<textarea type="text" id={id} value={value} rows={5} maxLength={maxLength} onChange={recalculate} textAreaLimit={textAreaLimit} />
</>
);
};
Method 3:
const Textarea = (props) => {
return (
<>
<textarea type="text" id={props.id} value={props.value} rows={5} maxLength={props.maxLength} onChange={recalculate} textAreaLimit={props.textAreaLimit} />
// Or Instead you can do as below
// <textarea type="text" rows={5} maxLength={5} onChange={recalculate} {...props} />
</>
);
};
I am trying to understand how to share hooks state across components. But it doesn't seem to be sharing. Am I doing something wrong here?
Home.js
export default function Home() {
const [search, setSearch]= useState('');
return (
<div>
<Input search={search} handleChange={setSearch} />
<Products search={search} handleChange={setSearch} />
</div>
)
}
Input.js
export default function Input({search, setSearch}) {
const handleChange = (e) => {
setSearch(e.target.value)
}
return (
<div className='App'>
<input
placeholder='search...'
value={search}
onChange={handleChange}
/>
{search}
</div>
)
}
Live Example:
const { useState } = React;
/*export default*/ function Home() {
const [search, setSearch]= useState('');
return (
<div>
<Input search={search} handleChange={setSearch} />
<Products search={search} handleChange={setSearch} />
</div>
)
}
/*export default*/ function Input({search, setSearch}) {
const handleChange = (e) => {
setSearch(e.target.value)
}
return (
<div className='App'>
<input
placeholder='search...'
value={search}
onChange={handleChange}
/>
{search}
</div>
)
}
const Products = ({search}) => {
return <div>Product: {search}</div>;
};
ReactDOM.createRoot(document.getElementById("root"))
.render(<Home />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.0.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.0.0/umd/react-dom.development.js"></script>
You pass handleSearch as prop in your Home component but Input is expecting setSearch, so just change this line in your Home
return (
<div>
<Input search={search} setSearch={setSearch} /> // change here
<Products search={search} handleChange={setSearch} />
</div>
)
I have a with the following structure in react...
<form>
<div>
<label>Name</label>
<input />
</div>
<div>
<label>Age</label>
<input />
</div>
<div>
<label>Gender</label>
<input />
</div>
<AddressComponent />
<button type='submit'>
Submit
</button>
</form>
Does anyone know if there is a way for me to pass the AddressComponent data up to the parent form component onSubmit? The AddressComponent has a lot of state to manage and a load of functions and it appears a few more times across my website and so I don't want to keep repeating the same code over and over again.
Anyone know if there's a way for me to pass the AddressComponent state up to the parent form component onSubmit?
you can use ref to get access to the child functions so:
const AddressComponent = ({}, ref) => {
const [data, setData] = useState();
useImperativeHandle(ref, () => ({
getData: getData,
}));
const getData = () => {
return data;
}
return <div></div>;
};
export default React.forwardRef(AddressComponent);
and you can get data in your parent and in submit like this:
const ParentComponent = () => {
const AddressRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
const data = AddressRef.current.getData();
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name</label>
<input />
</div>
<div>
<label>Age</label>
<input />
</div>
<div>
<label>Gender</label>
<input />
</div>
<AddressComponent ref={AddressRef} />
<button type='submit'>
Submit
</button>
</form>
)
}
I am trying to set the value inside an input in a Form component using KendoReact but the value is not displayed and upon debugging I see that the value is not defined. What am I doing wrong and what is the right approach here? Here is my code:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '#progress/kendo-react-form';
import { Input } from '#progress/kendo-react-inputs';
const InputField1 = (fieldRenderProps) => {
const { label, value } = fieldRenderProps;
return (
<div>
<Input label={label} value={value} />
</div>
);
};
const App = () => {
return (
<div>
<Form
render={(formRenderProps) => (
<>
<br />
<br />
<div>
<Field
name="Field1"
label="Field1 Label"
component={InputField1}
value="value1"
/>
</div>
<br />
<br />
</>
)}
/>
</div>
);
};
ReactDOM.render(<App />, document.querySelector('my-app'));
Here is a working codesandbox You have to pass the other props to the Input:
const InputField1 = (fieldRenderProps) => {
const { label, value, ...others } = fieldRenderProps;
console.log(value);
return (
<div>
<Input label={label} value={value} {...others} />
</div>
);
};
const MyCustomInput = (fieldRenderProps) => {
const {label, value, onChange} = fieldRenderProps;
return (
<Input label={label} value={value} onChange={onChange} />
);
};
const App = () => {
const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
return (
<Form
onSubmit={handleSubmit}
render={(formRenderProps) => (
<FormElement style={{maxWidth: 650}}>
<Field name={'firstName'} label={'First Name'} component={MyCustomInput} />
<div className="k-form-buttons">
<button type={'submit'} disabled=
{!formRenderProps.allowSubmit} className={'k-button'}>
Submit
</button>
</div>
</FormElement>
)}
/>
);
};
ReactDOM.render(
<App />,
document.querySelector('my-app')
);
you have to add onChange in input tag
So I've created inputs in two functions and on Onchange it gets the value that is being input into them. But anytime I type into the inputs I can only type a letter at a time before it is unfocused and I believe this is because the component keeps on rendering.
I've also realized that whenever I remove the Onchange and value props then everything goes back to normal and I'm able to type everything easily. How would I fix this?
App.js
import style from "./auth.module.css";
import { useEffect, useRef, useState } from "react";
import { useAuthState } from 'react-firebase-hooks/auth';
import { auth, signInWithEmailAndPassword, signInWithGoogle, registerWithEmailAndPassword } from "../../firebase";
import { CSSTransition } from "react-transition-group";
function Auth() {
const [activeMenu, setActiveMenu] = useState("main");
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [user, loading, error] = useAuthState(auth);
const Emailform = () => {
return (
<div className={style.formBox}>
<label className={style.label}>Email:</label>
<form className={style.input}>
<input
type="email"
name="email"
required="true"
value={email}
onChange={(e) => setEmail(e.target.value)} />
</form>
</div>
);
}
const Passform = () => {
return (
<div className={style.formBox}>
<label className={style.label}>Password:</label>
<form className={style.input}>
<input
type="password"
name="password"
required="true"
value={password}
onChange={(e) => setPassword(e.target.value)} />
</form>
</div>
);
}
let domNode = useClickOutside(() => {
setActiveMenu(false);
});
return (
<div className={style.container}>
<Login />
<Signup />
</div>
);
function AuthType(props) {
return (
<a
href={props.link}
className={style.menuItem}
onClick={() => props.goToMenu && setActiveMenu(props.goToMenu)}
>
{props.children}
</a>
);
}
/* Login */
function Login() {
return (
<CSSTransition in={activeMenu === "main"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.login}>
<h1 className={style.title}>Clip It</h1>
{/* Email and Password */}
<Emailform/>
<Passform/>
<div className={style.button}>
<input
type="submit"
value="Login"
onClick={() => signInWithEmailAndPassword(email, password)} />
<input
type="submit"
value="Login with Google"
onClick={signInWithGoogle} />
</div>
<div className={style.text}>
<p className={style.plink}>Forgot Password</p>
<div>
Need an account?
<AuthType goToMenu="Signup">click here</AuthType>
</div>
</div>
</div>
</div>
</CSSTransition>
);
}
function Signup() {
return (
<CSSTransition in={activeMenu === "Signup"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.signUp}>
<div className={style.title}> Clip It</div>
<Form label="First Name" type="text" />
<Form label="Last Name" type="Text" />
<Form label="Email" type="email"/>
<Form label="Date of Birth" type="date" />
<Form label="Password" type="password"/>
<Form label="Confirm Password" type="password" />
<div className={style.button}>
<input type="submit" value="Sign Up" />
</div>
<div className={style.text}>
have an
<AuthType goToMenu="main"> account</AuthType>
</div>
</div>
</div>
</CSSTransition>
);
}
}
let useClickOutside = (handler) => {
let domNode = useRef();
useEffect(() => {
let clickListener = (event) => {
if (domNode.current && !domNode.current.contains(event.target)) {
handler();
}
};
document.addEventListener("mousedown", clickListener);
return () => {
document.removeEventListener("mousedown", clickListener);
};
});
return domNode;
};
function Form(props) {
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type={props.input}
name={props.input}
required="true" />
</form>
</div>
);
}
export default Auth;
Don't put your component definition inside the other component. If you do it, you create a new component at every render.
You need to work around to move Emailform, Passform to outside of Auth.
Checkout this sandbox to experiment
function Auth() {
// 🚫 the bad place to define components
const Emailform = () => {
...
}
// 🚫 the bad place to define components
const Passform = () => {
...
}
}