Set state from props change in react functional component? - reactjs

I have simple react functional component looks like this following code
import React, { useState } from "react";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Test
children={
<React.Fragment>
<label>Name: </label>
<input type="text" onChange={(e) => setData(e)} />
</React.Fragment>
}
/>
</div>
);
}
export function Test({ children }) {
const [data, setData] = useState("");
return (
<>
<div>{children && children}</div>
<div>{data}</div>
</>
);
}
My question is how to update data state inside Test Component when onChange event triggered?
Here is the sandbox
Hopefully anyone can help me..
Thanks in advance

You dont have to send children that way. React has a specific way of handling children which is more easier to do and maintain. In your case you just have to lift you state up and send the state and callback as props.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [data, setData] = React.useState('')
return (
<div className="App">
<Test data={data}>
<> // shorthand for ReactFragment
<label>Name: </label>
<input type="text" onChange={(e) => setData(e.target.value)} value={data}/>
</>
</Test>
</div>
);
}
export function Test({ data, children }) {
return (
<>
<div>{children && children}</div>
<div>{data}</div>
</>
);
}

You can use use render props technique for this and you can can keep your pattern intact if you do not prefer major refactoring.
Working Demo - https://codesandbox.io/s/flamboyant-monad-hw2lu?file=/src/App.js
import React, { useState } from "react";
import "./styles.css";
export function Child(props) {
const { setData } = props;
return (
<React.Fragment>
<label>Name: </label>
<input type="text" onChange={(e) => setData(e.target.value)} />
</React.Fragment>
);
}
export default function App() {
return (
<div className="App">
<Test
render={(props) => {
const { setData } = props;
return <Child setData={setData} />;
}}
></Test>
</div>
);
}
export function Test(props) {
const [data, setData] = useState("");
const { render } = props;
return (
<>
<div>{render({ setData })}</div>
<div>{data}</div>
</>
);
}

Related

Refactor React Components Code Using Context API

I am suppose to refactor this code. The task is to avoid passing username, updateUsername all the way down and without passing props directly. I am suppose to do this without using any third party library or framework.
This is the code
import { useState } from "react";
export default function App() {
const [username, setUsername] = useState("owais");
const [otherUsername, setOtherUsername] = useState("calvin");
return (
<>
<User username={username} updateUsername={setUsername} />
<User username={otherUsername} updateUsername={setOtherUsername} />
</>
);
}
function User({ username, updateUsername }) {
return (
<div>
Username: {username}
<UsernameEditor username={username} updateUsername={updateUsername} />
</div>
);
}
function UsernameEditor({ username, updateUsername }) {
return (
<div>
<input
type="text"
value={username}
onChange={(e) => updateUsername(e.target.value)}
/>
</div>
);
}
I decided to use ContextAPI but I cannot seem to find a way to achieve this without either duplicating the user component and userNameEditor component code or passing the props directly to the component. Any ways I can achieve this?
This is what I currently have
import { createContext, useContext, useState } from "react"
interface AppContextInterface {
username: string;
setUsername: Function;
otherUsername: string;
setOtherUsername: Function;
}
const AppCtx = createContext<AppContextInterface | null>(null);
export default function App() {
const [username, setUsername] = useState("owais");
const [otherUsername, setOtherUsername] = useState("calvin");
const value = { username, setUsername, otherUsername, setOtherUsername };
return (
<AppCtx.Provider value={value}>
<User />
<OtherUser />
</AppCtx.Provider>
);
}
function UsernameEditor() {
const appContext = useContext(AppCtx);
return (
<div>
<input
type="text"
value={appContext?.username}
onChange={(e) => appContext?.setUsername(e.target.value)}
/>
</div>
);
}
function User() {
const appContext = useContext(AppCtx);
return (
<div>
Username: {appContext?.username}
<UsernameEditor />
</div>
);
}
function OtherUserEditor() {
const appContext = useContext(AppCtx);
return (
<div>
<input
type="text"
value={appContext?.otherUsername}
onChange={(e) => appContext?.setOtherUsername(e.target.value)}
/>
</div>
);
}
function OtherUser() {
const appContext = useContext(AppCtx);
return (
<div>
Username: {appContext?.otherUsername}
<OtherUserEditor />
</div>
);
}

React, Calling a function from another component

As stated in the question I want to call a function declared in another component. Here's some example data,
function BookingTable() {
const renderTableData = (startId) => {
let id = startId;
}
}
export default BookingTable;
How do i access the renderTableData from another component?
If the function should be accessible is the child component of the component which has the function. Then you can pass the function through props.
But the best option for this is context api. With that you can access the function in multiple components.
Context api helps you share the states and functions of a component
with other components inside the particular project.
In Filecontext.jsx you can see createContext which helps you in creating a context.
In App.jsx, we have created the states and functions which has to be shared among the components and wrapped the components which can access the datas with that context by importing it.
In Formcomponent.jsx, I am using useContext to use the states and functions created in the App.jsx.
Filecontext.jsx
import { createContext } from 'react'
export const Filecontext = createContext({});
App.jsx
import { Filecontext } from './Contexts/Filecontext';
import { useState } from 'react'
function App() {
const [name, setName] = useState("")
const [email, setEmail] = useState("")
const [mobileno, setMobileno] = useState("")
const showAlert = () => {
alert(`Hello ${name}`);
}
return (
<div className="App">
<Filecontext.Provider value={{ name, setName, email, setEmail, mobileno, setMobileno, showAlert }}>
<Formcomponent />
<Listcomponent />
</Filecontext.Provider>
</div>
);
}
export default App;
Formcomponent.jsx
import { Filecontext } from '../Contexts/Filecontext';
import { useContext } from 'react'
export default function Formcomponent() {
const { setName, setEmail, setMobileno, showAlert } = useContext(Filecontext)
return (
<>
<div className="form-group">
<label>Name : </label>
<input type="text" onChange={(e) => { setName(e.target.value) }} />
</div>
<div className="form-group">
<label>Email : </label>
<input type="email" onChange={(e) => { setEmail(e.target.value) }} />
</div>
<div className="form-group">
<label>Mobile No : </label>
<input type="number" onChange={(e) => { setMobileno(e.target.value) }} />
</div>
<div className="form-group">
<input type="submit" value="submit" onClick={() => { showAlert() }} />
</div>
</>
)
}
function BookingTable() {
const renderTableData = (startId) => {
let id = startId;
}
return (
<BookingTable2 renderTableData={renderTableData} />
)
}
export default BookingTable;
const BookingTable2 = ({renderTableData}) => {
const onClickHandler = () => {
renderTableData()
}
return (
<button onClick={onClickHandler}>Calling func from child component</button>
)
}
export default BookingTable;
Bear in mind that React FC are just JS functions that return JSX (in most cases) so you can't access variables that were declared inside of them from outside of the component. The solution to that problem would be to pass the function as props to the child components.

How do I transfer the value of the inputs from one page to another?

On one page I have two inputs and a button, after clicking on the button goes to the second page, how do I get data from the inputs on the second page?
navigate(path, { state: { input_value: value }}) ?
index.js
import './App.css';
function App() {
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'/>
<input type='text' placeholder='Surname'/>
<button type='submit'>Send</button>
</header>
</div>
);
}
export default App;
getData.js
export const getData = () => {
return (
<div>
<h1>Name:</h1>
<h1>Surname:</h1>
</div>
)
};
You can have a state variable in the App component and then pass the state as a prop to GetData:
import './App.css';
import { useState, useRef } from "react";
function App() {
const nameInput = useRef(null);
const surNameInput = useRef(null);
const [fullName, setFullName] = useState({
name: "",
surName: ""
});
const sendData = () => {
// you can do some input validation here
setFullName({
name: nameInput.current.value,
surName: surNameInput.current.value,
});
}
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'
ref={nameInput}
/>
<input type='text' placeholder='Surname'
ref={surNameInput}
/>
<button onClick={sendData}>Send</button>
</header>
<!-- some more markup here -->
</div>
);
}
export default App;
Here's how you pass your data to GetData component:
<GetData fullName={fullName} />
And then in your GetData component you get the passed props and display them:
export const GetData = (fullName) => {
return (
<div>
<h1>Name: {fullName.name}</h1>
<h1>Surname: {fullName.surName}</h1>
</div>
)
};
You can read more about hooks like useState and useRef here
So this might be Part 1 of an answer.
Taking the code you have, I've put the fields in form tag, then called handleSubmit from the built in onSubmit that is called when you click a button of type='submit' inside a form.
The values is taken from the event parameter (e) of the onSubmit (you can identify these by the 'name' attribute on the input tags) and then I am using useState hook to store the two values.
This would be where part one ends. You have the data, and you can see how it is passed to the GetDate component (the deconstructed props {name, surname} that are passed in.
From there, you should follow the documentation for your chosen router and, if you run into trouble, post the code you've tried and I can continue to help.
https://v5.reactrouter.com/web/guides/quick-start
import React, { useState } from 'react';
import { GetData } from './GetData';
export function App() {
const [theName, setTheName] = useState('');
const [theSurname, setTheSurname] = useState('');
const handleSubmit = (e) => {
setTheName(e.target.name.value);
setTheSurname(e.target.surname.value);
e.preventDefault();
}
return (
<div className='App'>
<header className='App-header'>
<form onSubmit={handleSubmit}>
<input type='text' placeholder='Name' name='name'/>
<input type='text' placeholder='Surname' name='surname'/>
<button type='submit'>Send</button>
</form>
</header>
<GetData name={theName} surname={theSurname} />
</div>
);
}
export default App;
Here is a component like your getData function.
I've added it to the App component, just so you can see the values being displayed, but for what you are looking for, you will need to read the documentation for react-router-dom
import React from "react";
export const GetData = ({name, surname}) => {
return (
<div>
<h1>Name:{name}</h1>
<h1>Surname:{surname}</h1>
</div>
)
};

Focus the Input automaticly in a react way every time I refresh the page

I can focus the input by get the element By ID, but is there a standard react way to do it?
function App() {
useEffect(() => {
let myInput = document.getElementById('myInput');
myInput.focus()
})
return (
<div className="App">
<input id="myInput" placeholder='your name'/>
</div>
);
}
Using useRef you can do it !
import React, { useEffect, useRef } from "react";
function App() {
const inputTxt = useRef(null)
useEffect(() => {
inputTxt.current.focus()
})
return (
<div>
<input type='text' />
<input ref={inputTxt} />
</div>
);
}
export default App;
We should never get DOM elements as these references might later give rise to memory leaks.
You could create a ref object and pass it to the input component and then use it later.
For example:
function App() {
const inputRef = React.useRef<HTMLElement>(null);
useEffect(() => {
inputRef.current.focus();
})
return (
<div className="App">
<input id="myInput" placeholder='your name' ref={inputRef}/>
</div>
);
}
Please read this doc for further information:
https://reactjs.org/docs/refs-and-the-dom.html

How to share Child component data into Parent Component uisng context API into Reactjs

I want to share Child Component (Product.js) data into Parent Component (App.js) without button click. I will use useState with context API. I don't know how to passed this data without click event.
Product.js data display into console.log() into App.js component.
App.js
import React from 'react';
import Product from './Product';
function App() {
return (
<div className="App">
<Product />
</div>
);
}
export default App;
Product.js
import React from 'react';
const Product = () => {
return (
<div>
Product Name: <input type="text" />
<br />
Description:- <input type="text" />
</div>
)
}
export default Product
Please help I have no advance knowledge of react.
There are three ways to share information without click event with useContext and useState.
useContext
useState in childComponent
useState in parentComponent
1. useContext
import { createContext, useContext, useState } from 'react';
import React from 'react';
const Context = createContext();
const Product = () => {
const [info, setInfo] = useContext(Context);
return (
<div>
Product Name: <input type="text" value={info.name} onChange={(e) => {
setInfo({ ...info, name: e.target.value });
}} />
<br />
Description:- <input type="text" value={info.desc} onChange={(e) => {
setInfo({ ...info, desc: e.target.value });
}} />
</div>
);
}
function App() {
const [info, setInfo] = useState({
name: '',
desc: ''
});
return (
<div className="App">
<Context.Provider value={[info, setInfo]}>
<Product />
</Context.Provider>
</div>
);
}
export default App;
If you have each component in a file. You have to create the context in a third file and import it from the parent and child component.
2. useState in childComponent
import { useEffect, useState } from 'react';
import React from 'react';
const Product = ({ setParentInfo }) => {
const [info, setInfo] = useState({ name: '', desc: '' });
useEffect(() => {
setParentInfo(info);
}, [info])
return (
<div>
Product Name: <input type="text" value={info.name} onChange={(e) => setInfo({ ...info, name: e.target.value })} />
<br />
Description:- <input type="text" value={info.desc} onChange={(e) => setInfo({ ...info, desc: e.target.value })} />
</div>
)
}
let info = { name: '', desc: '' }
function App() {
return (
<div className="App">
<Product setParentInfo={(newInfo) => {
info = { ...newInfo };
}} />
</div>
);
}
export default App;
3. useState in parentComponent
import { useState } from 'react';
import React from 'react';
const Product = ({ info, setParentInfo }) => {
return (
<div>
Product Name: <input type="text" value={info.name} onChange={(e) => setParentInfo({ ...info, name: e.target.value })} />
<br />
Description:- <input type="text" value={info.desc} onChange={(e) => setParentInfo({ ...info, desc: e.target.value })} />
</div>
)
}
function App() {
const [info, setInfo] = useState({ name: '', desc: '' });
console.log("parent: ", info);
return (
<div className="App">
<Product info={info} setParentInfo={(newInfo) => {
setInfo({ ...newInfo });
}} />
</div>
);
}
export default App;
I hope I've helped you. Have a nice day!
If its a direct child to parent communication, it's better to done this using props. Less code, works fine!
App.js:-
import React from 'react';
import Product from './Product';
function App() {
return (
<div className="App">
<Product
onProductNameChange={productName => console.log(productName)}
onProductDescChange={productDesc => console.log(productDesc)}
/>
</div>
);
}
export default App;
Product.js:-
import React from 'react';
const Product = ({onProductNameChange, onProductDescChange}) => {
return (
<div>
Product Name: <input type="text" onChange={e => onProductNameChange(e.target.value)} />
<br />
Description:- <input type="text" onChange={e => onProductDescChange(e.target.value)} />
</div>
)
}
export default Product
Share information without click event with useContext and useState.
App.js
import React from 'react';
import Store from './Store';
import ProductList from './ProductList';
function App() {
return (
<div className="App">
<Store>
<ProductList />
</Store>
</div>
);
}
export default App;
ProductList.js
import React, { useContext } from 'react';
import { Context } from './Store';
import Product from './Product';
import AddProduct from './AddProduct';
const ProductList = () => {
const [state, setState] = useContext(Context);
console.log(state);
return (
<>
<AddProduct />
<Product />
</>
)
}
export default ProductList
AddProduct.js
import React, { useContext} from 'react';
import { Context} from './Store';
const AddProduct = () => {
const [products, setProducts] = useContext(Context);
return (
<div>
Description:- <input type="text"
onChange={(e) => setProducts({ ...products, Description: e.target.value })}/>
</div>
)
}
export default AddProduct
Product.js
import React, { useContext} from 'react';
import { Context} from './Store';
const Product = () => {
const [products, setProducts] = useContext(Context);
return (
<>
<h2>Product</h2>
<p>Description: {products.Description}</p>
</>
)
}
export default Product
Store.js
import React, { useState } from 'react';
const product = {
Description: '',
};
export const Context = React.createContext();
const Store = ({ children}) => {
const [products, setProducts] = useState(product);
return (
<Context.Provider value={[products, setProducts]}>{children}</Context.Provider>
);
};
export default Store;

Resources