Why storing form data in useState() hook in React? - reactjs

I am somewhat familiar with React.
In react tutorials, I have seen people creating useState hooks to store form data and also making them controlled and started doing the same but recently I saw a video of coder creating a form without using any useState for storing the data?
This has left me stumped because I am not understanding why I need to store form data in useState hook now. For what type of situations, is it useful to use state or just simply take the form value?
Please help.
I have tried searching google, reading some blogs on useState hooks, how it is used where we want to re-render a component.
Like, If I am displaying some data, and want to change it on some event, then I will use useState hook so that it will re-render the display component, with the updated state.
But I am still not understanding its use case with forms.

In a React form, you may want to do something on the screen as a user is typing using an onChange callback. You would need to store the user's input in state in order to properly deal with it in a useEffect. Just storing naked variables in components in React is terrible practice.
You generally only use destructured constants or derived conditional variables after hooks are called in a component in order to affect the display or to pass hook results as input to other hooks.
I would be interested to see the video or tutorial that showed you how to do a form "without state" in React because I'm not sure how that's possible - unless they were using a third-party library like formik which abstracts the useState away from you through it's own methods.
*ADDED EXAMPLE
Consider this simple comparison of two forms, one with state and one without:
https://codesandbox.io/p/sandbox/zen-sammet-imfzl5
app.tsx
import { useState } from "react";
import "./App.css";
import FormWithState from "./FormWithState";
import FormWithoutState from "./FormWithoutState";
function App() {
return (
<>
<div
style={{
border: "1px solid green",
marginBottom: "1rem",
padding: "1rem",
}}
>
<div>
This form with state notifies after 3 characters, and on submit.
</div>
<FormWithState />
</div>
<div style={{ border: "1px solid red", padding: "1rem" }}>
<div>This form without state can't alert or useEffect</div>
<FormWithoutState />
</div>
</>
);
}
export default App;
FormWithState.tsx
import { useEffect, useState } from "react";
import "./App.css";
export default function FormWithState() {
const [message, setMessage] = useState("");
const onChange = (e: any) => {
setMessage(e.target.value);
};
const onSubmit = (e: any) => {
e.preventDefault();
alert(`You sent the message: ${message}`);
};
useEffect(() => {
if (message.length >= 3) {
alert("This message is longer than 3 characters. Wow!");
}
}, [message]);
return (
<div className="App">
<form onSubmit={onSubmit}>
<input type="text" name="name" onChange={onChange} />
<button type="submit">Submit</button>
</form>
</div>
);
}
FormWithoutState.tsx
import { useEffect } from "react";
import "./App.css";
export default function FormWithState() {
let message = "";
const onChange = (e: any) => {
message = e.target.value;
};
const onSubmit = (e: any) => {
e.preventDefault();
alert(`You sent the message: ${message}`);
};
useEffect(() => {
if (message.length > 3) {
alert("This message is longer than 3 characters. Wow!");
}
}, [message]);
return (
<div className="App">
<form onSubmit={onSubmit}>
<input type="text" name="name" onChange={onChange} />
<button type="submit">Submit</button>
</form>
</div>
);
}
Notice how the green form can alert and useEffect as the user types / submits - while the red form cannot.
Both Forms:
This works (with state):
This does not work (without state):
I hope that helps a bit!

Related

How to Add a CSS class to an element onClick with Next.JS

I migrated my app from React to Next Js. The final part I'm confused on is removing/adding classes on click of an element.
Essentially, I have an input element (and some other elements) that is set to display: none by default; on click, it should switch to display: block. This was working in React, but is not working after migration. The actual click event does work when I put an alert() within the function.
PLEASE HELP!!!!
Here is the onClick function:
searchActive = () =>{
if (typeof document !== "undefined"){
const searchForm = document.getElementById(styles["search-form"]);
searchForm.classList.remove(styles["display-none"]);
document.getElementById(styles["search-input-box"]).focus();
document.getElementById(styles["search-icon-id"]).classList.add(styles["display-none"]);
document.getElementById(styles["close-icon-id"]).classList.remove(styles["display-none"]);
}
}
UPDATED: This method of using useState also throws a scope error.
Component Function:
searchActive = () =>{
const [add, setAdd] = useState("none");
useState("block");
}
JSX:
<input style={{display: add}} onClick = {searchActive}/>
The code examples you gave are a bit short, so not sure what's happening there, but here is an example how to use the useState to manipulate style for example. Not the prettiest example, but one, as based on those pieces of code I think you might be using either the hooks or JSX in some incorrect way.
You should have a function that returns JSX, and in that function you should declare the state, or you could provide the state via props or context if needed, if the state is needed higher up in the hierarchy.
import { useState } from "react";
import "./styles.css";
export default function App() {
const [display, setDisplay] = useState(false);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={() => setDisplay((prevDisplay) => !prevDisplay)}>
Button
</button>
<p style={{ display: display ? "block" : "none" }}>something here</p>
</div>
);
}
Codesandbox

Reactjs Child Component not updating

I'm having trouble updating the state of the child component.
If i upload an image it doesn't appear in the ReactChromakeyedImage component. (which is a package i use)
import './App.css';
import ReactChromakeyedImage from 'react-chromakeyed-image';
import React, { useState, useEffect } from 'react';
function App() {
const [file, setFile] = useState();
function handleChange(e) {
console.log(e.target.files);
setFile(URL.createObjectURL(e.target.files[0]));
}
return (
<div className="App">
<h2>Add Image:</h2>
<input type="file" onChange={handleChange} />
<h3>Original</h3>
<img style={{width: "400px"}} src={file} />
<h3>Chromakeyed</h3>
<ReactChromakeyedImage src={file} style={{width: "400px"}} findColor="#3CC156" replaceColor="#FFFFFF" tolerance={70}/>
</div>
);
}
export default App;
I figured it out. It's a bug in the lib. Internally, it checks the img tag it uses inside to see if the complete property has been set on the image HTML element. This goes to true when the browser has painted the image. However, it only does this on every rerender, and it it also does not wait until it becomes true (it's asynchronous). That's why it's flaky.
Really the lib needs to be forked to fix, but there is a pretty horrible workaround you could do (this is really not nice at all, but it might be your only option unless you fork).
In this solution we attach a load listener using the DOM API (bad!) and then force a rerender, which triggers the library to check the completed property again.
Try this https://codesandbox.io/s/bold-voice-bhxw36?file=/src/App.js:0-1085.
import ReactChromakeyedImage from "react-chromakeyed-image";
import React, { useState, useRef, useLayoutEffect } from "react";
function App() {
const [file, setFile] = useState();
const containerRef = useRef();
const [, forceReload] = useState(0);
function handleChange(e) {
setFile(URL.createObjectURL(e.target.files[0]));
}
useLayoutEffect(() => {
if (!containerRef.current) return;
containerRef.current.querySelector("img").addEventListener("load", () => {
forceReload((x) => x + 1);
});
}, [file]);
return (
<div className="App">
<h2>Add Image:</h2>
<input type="file" onChange={handleChange} />
<h3>Original</h3>
<img style={{width: "400px"}} src={file} />
<h3>Chromakeyed</h3>
{file && (
<div ref={containerRef}>
<ReactChromakeyedImage
src={file}
findColor="#3CC156"
replaceColor="#FFFFFF"
tolerance={70}
width="400"
height="400"
/>
</div>
)}
</div>
);
}
export default App;
I do not know this library, and it could be a bug or simply that values are just stored and don't get updated if you change the value of the image. My advice would be just to make the component re-render to update it. The simplest way is to add a key to it, whenever this key changes the component will get re-rendered. I use file.name in this example but any changing value would be fine.
<ReactChromakeyedImage key={file.name} src={file} style={{width: "400px"}} findColor="#3CC156" replaceColor="#FFFFFF" tolerance={70}/>

How to access variable declared inside a functional component in other functions

Stuffing the functions inside FoodSearch() isn't good practice since the other functions would be defined every time the component is rendered. However, when I put the functions outside of FoodSearch(), the variables become undefined.
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
function handleChange(e) {
setQuery(e.target.value);
}
function handleClick(e) {
// e.preventDefault();
history(`/food/${query}`);
}
function FoodSearch() {
const [query, setQuery] = useState('');
const history = useNavigate()
return (
<div>
<h1>Search For A Food!</h1>
<input
type='text'
placeholder='search for a food'
value={query}
onChange={handleChange}
/>
<Link to={`/food/${query}`}>Go!</Link>
<button onClick={handleClick}>Save New Food!</button>
</div>
);
}
export default FoodSearch;
I'm a beginner in react and trying to learn
You should define them inside FoodSearch but wrap in a useCallback to memoize the returned function. That way they won't change unless a dependency (e.g. query) changes.

UseEffect and useCallback still causes infinite loop in react project

I can't seem to resolve an infinite loop issue in my react project.
I'm working on a daily-log react app. Let me explain the project briefly. Here is the picture of the code for quick view:
The same code is available at the bottom.
The structure (from top to bottom)
The DailyLog component has a form that uses Question components into which props are passed.
The Question component uses the props to display a question and description. It also contains an Input component into which props are further passed down.
The Input component takes the props and renders the appropriate form input field.
The logic (from bottom to top)
The Input component handles it's own inputState. The state is changed when the user inputs something and the onChangeHandler is triggered.
The Input component also has a useEffect() hook that calls an onInput() function that was passed down as props from DailyLog.
The onInputHandler() in the DailyLog component updates the formState which is the form-wide state containing all input field values. The formState is amended depending on which input field is filled at the time.
The onInputHandler() uses the useCallback() hook which is supposed to stop an infinite loop caused by any parent/child re-renders. But it doesn't work :frowning:
What's wrong in the code? What am I missing here? Code provided below:
//DailyLog.js
import React, { useState, useCallback } from 'react';
import Question from '../components/FormElements/Question';
import questionData from '../components/DailyLog/questionData';
import './DailyLog.css';
const DailyLog = () => {
const [formState, setFormState] = useState();
const onInputHandler = useCallback(
(inputId, inputValue) => {
setFormState({
...formState,
[inputId]: inputValue,
});
},
[formState]
);
return (
<main className="container">
<form action="" className="form">
<Question
id="title"
element="input"
type="text"
placeholder="Day, date, calendar scheme"
onInput={onInputHandler}
/>
<Question
id="focus"
question={questionData.focusQuestion}
description={questionData.focusDescription}
element="textarea"
placeholder="This month's focus is... This week's focus is..."
onInput={onInputHandler}
/>
</form>
</main>
);
};
export default DailyLog;
//Question.js
import React from 'react';
import Input from './Input';
import './Question.css';
const Question = props => {
return (
<div className="form__group">
{props.question && (
<label className="form__label">
<h2>{props.question}</h2>
</label>
)}
<small className="form__description">{props.description}</small>
<Input
id={props.id}
element={props.element}
type={props.type}
placeholder={props.placeholder}
onInput={props.onInput}
/>
</div>
);
};
export default Question;
//Input.js
import React, { useState, useEffect } from 'react';
import './Input.css';
const Input = props => {
const [inputState, setInputState] = useState();
const { id, onInput } = props;
useEffect(() => {
onInput(id, inputState);
}, [id, onInput, inputState]);
const onChangeHandler = event => {
setInputState(event.target.value);
};
// check if question element type is for input or textarea
const element =
props.element === 'input' ? (
<input
id={props.id}
className="form__field"
type={props.type}
value={inputState}
placeholder={props.placeholder}
onChange={onChangeHandler}
/>
) : (
<textarea
id={props.id}
className="form__field"
rows="1"
value={inputState}
placeholder={props.placeholder}
onChange={onChangeHandler}
/>
);
return <>{element}</>;
};
export default Input;
Remove id and onInput from useEffect sensivity list
useEffect(() => {
onInput(id, inputState);
}, [inputState]);
And set default value of inputState to '' as follow:
const [inputState, setInputState] = useState('');
To prevent 'A component is changing an uncontrolled input of type text to be controlled error in ReactJS'. Also you can init formState:
const [formState, setFormState] = useState({title:'', focus:''});

When to use useImperativeHandle, useLayoutEffect, and useDebugValue

I cannot understand why the following useImperativeHandle, useLayoutEffect, and useDebugValue hooks are needed, can you give examples when they can be used, but not examples from the documentation please.
Allow me to preface this answer by stating that all of these hooks are very rarely used. 99% of the time, you won't need these. They are only meant to cover some rare corner-case scenarios.
useImperativeHandle
Usually when you use useRef you are given the instance value of the component the ref is attached to. This allows you to interact with the DOM element directly.
useImperativeHandle is very similar, but it lets you do two things:
It gives you control over the value that is returned. Instead of returning the instance element, you explicitly state what the return value will be (see snippet below).
It allows you to replace native functions (such as blur, focus, etc) with functions of your own, thus allowing side-effects to the normal behavior, or a different behavior altogether. Though, you can call the function whatever you like.
There could be many reasons you want might to do either of the above; you might not want to expose native properties to the parent or maybe you want to change the behavior of a native function. There could be many reasons. However, useImperativeHandle is rarely used.
useImperativeHandle customizes the instance value that is exposed to parent components when using ref
Example
In this example, the value we'll get from the ref will only contain the function blur which we declared in our useImperativeHandle. It will not contain any other properties (I am logging the value to demonstrate this). The function itself is also "customized" to behave differently than what you'd normally expect. Here, it sets document.title and blurs the input when blur is invoked.
const MyInput = React.forwardRef((props, ref) => {
const [val, setVal] = React.useState('');
const inputRef = React.useRef();
React.useImperativeHandle(ref, () => ({
blur: () => {
document.title = val;
inputRef.current.blur();
}
}));
return (
<input
ref={inputRef}
val={val}
onChange={e => setVal(e.target.value)}
{...props}
/>
);
});
const App = () => {
const ref = React.useRef(null);
const onBlur = () => {
console.log(ref.current); // Only contains one property!
ref.current.blur();
};
return <MyInput ref={ref} onBlur={onBlur} />;
};
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
useLayoutEffect
While similar to some extent to useEffect(), it differs in that it will run after React has committed updates to the DOM. Used in rare cases when you need to calculate the distance between elements after an update or do other post-update calculations / side-effects.
The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
Example
Suppose you have an absolutely positioned element whose height might vary and you want to position another div beneath it. You could use getBoundingClientRect() to calculate the parent's height and top properties and then just apply those to the top property of the child.
Here you would want to use useLayoutEffect rather than useEffect. See why in the examples below:
With useEffect: (notice the jumpy behavior)
const Message = ({boxRef, children}) => {
const msgRef = React.useRef(null);
React.useEffect(() => {
const rect = boxRef.current.getBoundingClientRect();
msgRef.current.style.top = `${rect.height + rect.top}px`;
}, []);
return <span ref={msgRef} className="msg">{children}</span>;
};
const App = () => {
const [show, setShow] = React.useState(false);
const boxRef = React.useRef(null);
return (
<div>
<div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
{show && <Message boxRef={boxRef}>Foo bar baz</Message>}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
.box {
position: absolute;
width: 100px;
height: 100px;
background: green;
color: white;
}
.msg {
position: relative;
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
With useLayoutEffect:
const Message = ({boxRef, children}) => {
const msgRef = React.useRef(null);
React.useLayoutEffect(() => {
const rect = boxRef.current.getBoundingClientRect();
msgRef.current.style.top = `${rect.height + rect.top}px`;
}, []);
return <span ref={msgRef} className="msg">{children}</span>;
};
const App = () => {
const [show, setShow] = React.useState(false);
const boxRef = React.useRef(null);
return (
<div>
<div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
{show && <Message boxRef={boxRef}>Foo bar baz</Message>}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
.box {
position: absolute;
width: 100px;
height: 100px;
background: green;
color: white;
}
.msg {
position: relative;
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
useDebugValue
Sometimes you might want to debug certain values or properties, but doing so might require expensive operations which might impact performance.
useDebugValue is only called when the React DevTools are open and the related hook is inspected, preventing any impact on performance.
useDebugValue can be used to display a label for custom hooks in React DevTools.
I have personally never used this hook though. Maybe someone in the comments can give some insight with a good example.
useImperativeHandle
useImperativeHandle allows you to determine which properties will be exposed on a ref. In the example below, we have a button component, and we'd like to expose the someExposedProperty property on that ref:
[index.tsx]
import React, { useRef } from "react";
import { render } from "react-dom";
import Button from "./Button";
import "./styles.css";
function App() {
const buttonRef = useRef(null);
const handleClick = () => {
console.log(Object.keys(buttonRef.current)); // ['someExposedProperty']
console.log("click in index.tsx");
buttonRef.current.someExposedProperty();
};
return (
<div>
<Button onClick={handleClick} ref={buttonRef} />
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
[Button.tsx]
import React, { useRef, useImperativeHandle, forwardRef } from "react";
function Button(props, ref) {
const buttonRef = useRef();
useImperativeHandle(ref, () => ({
someExposedProperty: () => {
console.log(`we're inside the exposed property function!`);
}
}));
return (
<button ref={buttonRef} {...props}>
Button
</button>
);
}
export default forwardRef(Button);
Available here.
useLayoutEffect
This is the same as useEffect, but only fires once all DOM mutations are completed. This article From Kent C. Dodds explains the difference as well as anyone, regarding these two, he says:
99% of the time [useEffect] is what you want to use.
I haven't seen any examples which illustrate this particularly well, and I'm not sure I'd be able to create anything either. It's probably best to say that you ought to only use useLayoutEffect when useEffect has issues.
useDebugValue
I feel like the docs do a pretty good example of explaining this one. If you have a custom hook, and you'd like to label it within React DevTools, then this is what you use.
If you have any specific issues with this then it'd probably be best to either comment or ask another question, because I feel like anything people put here will just be reiterating the docs, at least until we reach a more specific problem.
The useImperativeHandle hook helped me a lot with a use case of mine.
I created a grid component which uses a third-party library component. The library itself has a big data layer with built-in functionality that can be used by accessing the instance of the element.
However, in my own grid component, I want to extend it with methods which perform actions on the grid. Furthermore, I also want to be able to execute those methods from outside of my grid component.
This is easily achievable by adding the methods inside of the useImperativeHandle hook, and then they will be exposed and usable by its parent.
My grid component looks kind of like this:
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import ThirdPartyGrid from 'some-library';
export default forwardRef((props, forwardedRef) => {
const gridRef = useRef(null);
useImperativeHandle(forwardedRef,
() => ({
storeExpandedRecords () {
// Some code
},
restoreExpandedRecords () {
// Some code
},
}));
return (
<div ref={forwardedRef}>
<ThirdPartyGrid
ref={gridRef}
{...props.config}
/>
</div>
);
});
And then in my parent, I can execute those methods:
import React, { useRef, useEffect } from 'react';
export default function Parent () {
const gridRef = useRef(null);
const storeRecords = () => {
gridRef.current.storeExpandedRecords();
};
useEffect(() => {
storeRecords();
}, []);
return <GridWrapper ref={gridRef} config={{ something: true }} />
};
useImperativeHandle
usually hook expose your functional based component method and properties to other component by putting functional component inside forwardRef
example
const Sidebar=forwardRef((props,ref)=>{
const [visibility,setVisibility]=useState(null)
const opensideBar=()=>{
setVisibility(!visibility)
}
useImperativeHandle(ref,()=>({
opensideBar:()=>{
set()
}
}))
return(
<Fragment>
<button onClick={opensideBar}>SHOW_SIDEBAR</button>
{visibility==true?(
<aside className="sidebar">
<ul className="list-group ">
<li className=" list-group-item">HOME</li>
<li className=" list-group-item">ABOUT</li>
<li className=" list-group-item">SERVICES</li>
<li className=" list-group-item">CONTACT</li>
<li className=" list-group-item">GALLERY</li>
</ul>
</aside>
):null}
</Fragment>
)
}
//using sidebar component
class Main extends Component{
myRef=createRef();
render(){
return(
<Fragment>
<button onClick={()=>{
///hear we calling sidebar component
this.myRef?.current?.opensideBar()
}}>
Show Sidebar
</button>
<Sidebar ref={this.myRef}/>
</Fragment>
)
}
}
I see that there are already answers to this question. So, I just want to share my articles about useImperativeHandle, useEffect, and useLayoutEffect.
I have written an example-based article aboutuseImperativeHandle hook, you can check it out here: useImperativeHandle by Examples
I also wrote an article about effects in React which explains useEffect hook and useLayoutEffect hook differences in detail: A Beginner’s Guide to Effects in React

Resources