I've been trying to find a solution for this, where I'd like to add the focus state to my input field. But it doesn't seem to be working:
const textareaRef = useRef<HTMLInputElement>(null);
...
const handleClick = () => {
if (textareaRef.current) {
alert('Field clicked');
textareaRef.current.focus();
textareaRef.current.click();
}
};
...
<input
ref={textareaRef}
onClick={handleClick}
onFocus={(e) => e.persist()}
spellCheck="false"
/>
This doesn't work, and the main reason is to show the mobile keyboard. See video here of it not working https://share.getcloudapp.com/jkuYLqO5 and here without alert https://share.getcloudapp.com/wbuKwrLE
I cannot reproduce the bug on desktop browser. What browser is it in the screen recording?
function App() {
const textareaRef = React.useRef();
const handleClick = () => {
if (textareaRef.current) {
console.log('Field clicked');
textareaRef.current.focus();
}
};
return (
<div>
<div onClick={handleClick}>click me to focus input</div>
<input
ref={textareaRef}
onFocus={(e) => e.persist()}
/>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script src="https://unpkg.com/react#16.14.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16.14.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can do this in useEffect() Hook. Below component mocks your scenario. As the component loads, I make focus() appear on input.
import React, { useEffect, useRef } from 'react';
const UseRefBasics = () => {
const refContainer = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
console.log(refContainer.current.value);
};
useEffect(() => {
console.log(refContainer.current);
refContainer.current.focus();
});
return (
<>
<form className='form' onSubmit={handleSubmit}>
<div>
<input type='text' ref={refContainer} />
</div>
<button type='submit'>submit</button>
</form>
</>
);
};
export default UseRefBasics;
This is a general scenario that I have given. You may use useEffect() and get your focus when the state updates by providing another argument to useEffect(()=>{},[state1]) or in whatever way you need it.
Related
Counter File
import React, { useState } from "react";
import Widget from "./widget";
const Counter = () => {
const [form, setForm] = useState(<></>)
const [text, setText] = useState("")
const onCounterChange =() => {
setText(text)
}
const formLoad =() =>{
setForm(
<Widget
onCounterChange={onCounterChange}
children={
<input type="text" onChange={(e) =>{
setText(e.target.value)
}}/>
}
/>
)
}
return (
<div>
{text}
<button onClick={formLoad}>
load widget
</button>
{form}
</div>
)
}
export default Counter
Widget File
import React from 'react'
export default function Widget(props) {
return (
<div className="buttons">
{props.children}
<button onClick={props.onCounterChange}>Save</button>
</div>
)
}
I have created small text printing page . for some purpose I have added children in a diff component and handling widget in a state , so when I try to change the data , text state is changing but when I click save text state becomes empty
As I mentioned in my comment putting a component in state probably isn't the best way of approaching this. Instead I would have a boolean state that allows you to toggle the component on/off.
const { useState } = React;
function Example() {
const [ showWidget, setShowWidget ] = useState(false);
const [ text, setText ] = useState('');
function handleChange(e) {
setText(e.target.value);
}
function handleClick() {
setShowWidget(!showWidget);
}
function handleSave() {
console.log(`Saved state: ${text}`);
}
return (
<div>
<p className="text">Current state: {text}</p>
<button onClick={handleClick}>
Load widget
</button>
{showWidget && (
<Widget>
<input
type="text"
onChange={handleChange}
/>
<button onClick={handleSave}>Save</button>
</Widget>
)}
</div>
);
}
function Widget({ children }) {
return <div className="widget">{children}</div>;
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
.widget { margin-top: 1em; }
.text { margin-bottom: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I was unable to find good answer to the question of focusing and selecting an input element on load; initially I just used a useRef(), set it as the ref and then on component load (useEffect(,[])) I would ref.current.focus() / ref.current.select(). But input's built in autoFocus and onFocus are much simpler solutions.
In other words, consider the following code snippet where we want to focus on the input when you press the button (it doesn't work here)
// Get hook functions (specific for use with stackoverflow code snippet)
const { useState } = React;
const Example = ({title}) => {
// hook that keeps track of the editing state
const [editing, setEditing] = useState(false);
// switch to input when button is pressed
const InputComponent = () => {
if (!editing) {
return ( <span>click for input</span>)
}
else {
return ( <input /> )
}
}
return (
<div>
<p>{title}</p>
<p>{InputComponent()}</p>
<button onClick={() => setEditing(!editing)}>
Click me
</button>
</div>
);
};
// Render it
ReactDOM.render(
<Example title="The goal is to have the input automatically focused" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Now, if we use references
const { useState, useRef, useEffect } = React;
const Example = ({title}) => {
const [editing, setEditing] = useState(false);
// useRef for a reference to the input
const inputRef = useRef();
const InputComponent = () => {
if (!editing) {
return ( <span>click for input</span>)
}
else {
// set the ref as a reference to the input
return ( <input ref={inputRef}/> )
}
}
// when editing updates, run this code
useEffect(() => {
// when editing is true, focus the input
if (editing) {inputRef.current.focus()}
}, [editing])
return (
<div>
<p>{title}</p>
<p>{InputComponent()}</p>
<button onClick={() => setEditing(!editing)}>
Click me
</button>
</div>
);
};
// Render it
ReactDOM.render(
<Example title="The goal is to have the input automatically focused" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
further if you want to select the value of the input when it's focused you can do the following:
const { useState, useRef, useEffect } = React;
const Example = ({title}) => {
const [editing, setEditing] = useState(false);
// define a state variable for the input value
const [inputValue, setValue] = useState("value of the input");
const inputRef = useRef();
const InputComponent = () => {
if (!editing) {
return ( <span>click for input</span>)
}
else {
return (
<input
ref={inputRef}
// define the value of the input
value={inputValue}
// when the input is changed, update the state variable
onChange={(event) => setValue(event.target.value)}
/>
)
}
}
useEffect(() => {
if (editing) {
inputRef.current.focus();
// focus and select the value of the input
inputRef.current.select();
}
}, [editing])
return (
<div>
<p>{title}</p>
<p>{InputComponent()}</p>
<button onClick={() => setEditing(!editing)}>
Click me
</button>
</div>
);
};
// Render it
ReactDOM.render(
<Example title="The goal is to have the input automatically focused" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
This methodology would allow you to set some base value of the input (like a stylized way of representing the value, and then when the user clicks that stylize value it begins editing).
This solution is well and good, but it's bulky and requires that you pass references down to the component in some cases, and is just generally not clean. So is there a better solution? yes....
Here's a simplified version that will accomplish the same effect but much simpler
const { useState, useRef, useEffect } = React;
const Example = ({title}) => {
const [editing, setEditing] = useState(false);
const [inputValue, setValue] = useState("value of the input");
// no need for a reference
const InputComponent = () => {
if (!editing) {
return ( <span>click for input</span>)
}
else {
return (
<input
// don't need to set an input reference
onChange={(event) => setValue(event.target.value)}
value={inputValue}
onFocus={(event) => event.target.select()} // selects the value on load
autoFocus // focuses on load
/>
)
}
}
// no need for a useEffect
return (
<div>
<p>{title}</p>
<p>{InputComponent()}</p>
<button onClick={() => setEditing(!editing)}>
Click me
</button>
</div>
);
};
// Render it
ReactDOM.render(
<Example title="The goal is to have the input automatically focused" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
There you go, a much faster, simpler, and easier to understand implementation of focusing on an input on load. Cheers! :)
Starting out with react, I have created an input field in the parent component, When the user submits the text, I am passing down the text to the child component and printing the text under the parent component.
Code of parent component
import React, { useState } from "react";
import Validate from "./Validate";
function CommandEntry() {
const [value, setValue] = useState("");
const handleSubmit = e => {
e.preventDefault();
// console.log(value);
return <Validate value={value} />;
};
return (
<div>
<form onSubmit={handleSubmit}>
enter text:~
<input
type="text"
autoFocus
required
onChange={e => setValue(e.target.value)}
/>
</form>
</div>
);
}
export default CommandEntry;
Code of child component
import React from "react";
export default function Validate(props) {
console.log("I am in child");
return (
<div>
<h1>{props.value}</h1>
</div>
);
}
You would need to render the child inside return and the set the value in handler.
Like so:
function CommandEntry() {
const [value, setValue] = useState("");
const [submit, setSubmitValue] = useState(false);
const handleChange = e => {
e.preventDefault();
setValue(e.target.value); //setting the value on change
};
const handleSubmit = e => {
e.preventDefault();
setSubmitValue(true); //setting the submit flag to true.
};
return (
<div>
value &&
<form onSubmit={handleSubmit}> // submit handler
enter text:~
<input
type="text"
autoFocus
required
onChange={handleChange} // change handler
/>
{submit &&
<Validate value={value} /> // rendering the child component
}
</form>
</div>
);
}
You can't return a JSX element from a handler function. Handler functions are different and render functions are different. So here you can change this in your parent component in which child will be shown only when submit is true.
import React, { useState } from "react";
import Validate from "./Validate";
function CommandEntry() {
const [value, setValue] = useState("");
const [submit, setSubmitValue] = useState(false);
const handleSubmit = e => {
e.preventDefault();
setSubmitValue(true);
};
return (
<div>
<form onSubmit={handleSubmit}>
enter text:~
<input
type="text"
autoFocus
required
onChange={e => setValue(e.target.value)}
/>
</form>
{submit && <Validate value={value} />}
</div>
);
}
export default CommandEntry;
I am using react for the first time in months and hooks for the first time ever. So far I love it, but I ran into an issue that has blocked me for hours now.
I have a component that looks like this:
import React, { useState } from "react";
import ReactModal from "react-modal";
import { useModal } from "react-modal-hook";
import styled from 'styled-components';
export function SearchImageForm(props) {
const [query, setQuery] = useState("");
const [images, setImages] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
fetch(`myurl&q=${query}`)
.then((response) => {
return response.json();
})
.then((myJson) => {
if (myJson.totalHits > 0) {
setImages(myJson.hits);
showModal(images, handleClick);
}
});
};
const FormWrapper = styled.div`
text-align: left;
`;
const Container = styled.div``;
const LabelEl = styled.label``;
const Input = styled.input``;
const handleClick = (e) => {
const urlField = document.querySelector('#url');
urlField.value = e.target.src;
urlField.select();
document.execCommand("copy");
alert('URL has been copied to your clipboard. You may now paste it into the URL field.')
};
return (
<Container>
<FormWrapper>
<form onSubmit={handleSubmit}>
<LabelEl>
Enter Search Term
<Input type="text" name="query" value={query} onChange={e => setQuery(e.target.value)} />
</LabelEl>
<input type="submit" value="Submit" />
</form>
</FormWrapper>
</Container>
);
}
export default SearchImageForm;
const ImageResult = ({content, handleClick}) => (
<picture>
<img src={content.previewURL} alt="" onClick={handleClick}/>
</picture>
);
const [showModal, hideModal] = useModal(({images, handleClick}) => (<ReactModal isOpen ariaHideApp={false}>
<p>Found {images.length} image(s). Click an image to copy its URL to your clipboard.</p>
{images.map(image => <ImageResult key={image.id} content={image} handleClick={handleClick} />)}
<input type="text" name="url" id="url" key="url" />
<button onClick={hideModal}>Hide modal</button>
</ReactModal>));
When I try to run it, I get the React hooks error (Invalid Hook Call). Any ideas on what I am doing wrong here?
Here is the code on codesandbox, though i am not sure why it wont run.
https://codesandbox.io/s/blue-firefly-0sx6h
Thanks!
The useModal hook needs to be called inside your component.
From the Invalid Hook Call Warning documentation:
Hooks can only be called inside the body of a function component.
So the modification what you need to do is the following:
export function SearchImageForm(props) {
const [showModal, hideModal] = useModal(({images, handleClick}) => (
<ReactModal isOpen ariaHideApp={false}>
<p>Found {images.length} image(s). Click an image to copy its URL to your clipboard. </p>
{images.map(image => <ImageResult key={image.id} content={image} handleClick={handleClick} />)}
<input type="text" name="url" id="url" key="url" />
<button onClick={hideModal}>Hide modal</button>
</ReactModal>
));
// ... rest of your component code
return <>{/* your render */}</>
}
I hope that helps!
I am new to React. I am working on a piece of code. I am trying to make a ToDoList kind of thing. So i have created 3 different component , one for taking input and one for displaying the entered code along with App.jsx.
Here are the code for each.
App.jsx
import React, { useState } from "react";
import Header from "./Header";
import Footer from "./Footer";
import Note from "./Note";
import CreateArea from "./CreateArea";
function App() {
const [arr, setArr] = useState([]);
function getData(note) {
// setArr(previous => [...previous, { title: newTitle, content: newContent }]);
setArr(previous => {return [...previous, note];});
console.log(arr);
}
return (
<div>
<Header />
<CreateArea reply={getData} />
{arr.map((arry, index) => (
<Note
key={index}
id={index}
title={arry.title}
content={arry.content}
/>
))}
<Footer />
</div>
);
}
export default App;
CreateArea.jsx
import React, { useState } from "react";
function CreateArea(props) {
const [note, setNote] = useState({
title: "",
content: ""
});
function handleChange(event) {
const { name, value } = event.target;
setNote(prevNote => {
return {
...prevNote,
[name]: value
};
});
}
function submitNote(event) {
// const newTitle = note.title;
// const newContent = note.content;
// props.reply(newTitle, newContent);
props.reply(note);
setNote({
title: "",
content: ""
});
event.preventDefault();
}
return (
<div>
<form>
<input
name="title"
onChange={handleChange}
value={note.title}
placeholder="Title"
/>
<textarea
name="content"
onChange={handleChange}
value={note.content}
placeholder="Take a note..."
rows="3"
/>
<button onClick={submitNote}>Add</button>
</form>
</div>
);
}
export default CreateArea;
Note.jsx
import React from "react";
function Note(props) {
return (
<div className="note">
<h1>{props.title}</h1>
<p>{props.content}</p>
<button>DELETE</button>
</div>
);
}
export default Note;
Now somehow i am getting the result right and the new post are being added as required but when I do a console.log for the array which stores inside getData() in App.jsx ,I observe there is a delayy of one click before the array is being added.
Sample problem
In the images attached.I have added new post with random data but when i see the console log the array is still empty. When i click the Add button for the second time , only then after second click on adding new post is the data being shown. I can't seem to figure out the reasoning behind it. Am I doing something wrong or missing something ?
Your setArr() from useState() is an asynchronous function. Hence, the console.log(arr) is called before the state is actually updated.
However, you can log arr in the useEffect() hook which is called on every state change, like this:
useEffect(() => {console.log(arr)})
You can find more information about this hook at https://reactjs.org/docs/hooks-effect.html.
This is delay is because useState mutation is asynchronous, you cannot get updated result in one tick.
try console.log result in useEffect hook like below:
function App() {
const [arr, setArr] = useState([]);
function getData(note) {
// setArr(previous => [...previous, { title: newTitle, content: newContent }]);
setArr(previous => {
return [...previous, note];
});
}
useEffect(() => {
console.log(arr);
});
return (
<div>
<CreateArea reply={getData} />
{arr.map((arry, index) => (
<Note
key={index}
id={index}
title={arry.title}
content={arry.content}
/>
))}
</div>
);
}
more info: hooks-reference.html#useeffect