Not able to focus on input when i use useImperativeHandle - reactjs

Here is my code: I have created the input component and header component. if I comment on the useImperativeHandle hook, input focus is working fine. please check this code.
import { forwardRef, useImperativeHandle, useRef } from "react";
const Input = forwardRef((props, ref) => {
// here is useImperativeHandle
useImperativeHandle(ref, () => ({
// ```method```
someExposedProperty: () => {
alert("inside the exposed property function!");
}
}));
// Return statement
return (
<>
<input type="text" ref={ref} />
</>
);
});
// header component
export default function Header() {
const inputRef = useRef();
return (
<>
<div className="container">
<Input ref={inputRef} />
</div>
<button
// Click function.
onClick={() => {
inputRef.current.focus();
}}
>
Add focus
</button>
</>
);
}

You need two refs here. One to bind the functions (which are going to expose to the outside) and another to keep a reference to the input element (this one is only used inside the Input and not exposed to outside components).
Try like below:
import { forwardRef, useImperativeHandle, useRef } from "react";
const Input = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(
ref,
() => ({
focus: () => {
inputRef.current.focus();
}
}),
[]
);
return (
<>
<input type="text" ref={inputRef} />
</>
);
});
export default function Header() {
const inputRef = useRef();
return (
<>
<div className="container">
<Input ref={inputRef} />
</div>
<button
onClick={() => {
inputRef.current.focus();
}}
>
Add focus
</button>
</>
);
}
Working Demo

Related

React with Typescript functional components - how to call child method from parent

I have this simplified structure:
<Page>
<Modal>
<Form />
</Modal>
</Page>
All of these are functional components.
And in <Modal /> I have a close function that looks like this:
const close = () => {
// apply a CSS class - so the modal disappears animatedly ..
// then use setTimeout() to completely remove the modal after the animation ends ..
}
Do you have an idea how the <Page /> component can call the <Modal /> close method? And the page has to do it because this is where I'm doing the call to API with the data from the form, and so if all is OK with API request - close the modal.
(The <Form /> handles only the form validation but then passes the data to <Page /> where all the business logic happens.)
PS: The project uses Typescript ... so I have to deal with types as well :(
I look into your problem. I think my example should clarify your problem. Let me know if you have any questions.
import { ReactNode, useCallback, useEffect, useState } from 'react'
import { render } from 'react-dom'
function App() {
return (
<div>
<Page />
</div>
)
}
function Page() {
const [isModalOpen, setModalOpen] = useState(false)
const handleFormSubmit = useCallback((formValues: FormValues) => {
console.log({ formValues })
setModalOpen(false)
}, [])
return (
<div>
<button onClick={() => setModalOpen(!isModalOpen)}>Toggle modal</button>
<Modal isOpen={isModalOpen}>
<Form onSubmit={handleFormSubmit} />
</Modal>
</div>
)
}
interface ModalProps {
isOpen: boolean
children: ReactNode
}
function Modal(props: ModalProps) {
const [isOpen, setIsOpen] = useState(false)
const close = () => {
setIsOpen(false)
}
const open = () => {
setIsOpen(true)
}
useEffect(() => {
if (!props.isOpen && isOpen) close()
if (props.isOpen && !isOpen) open()
}, [props.isOpen])
if (!isOpen) return null
return <div className="modal">{props.children}</div>
}
interface FormProps {
onSubmit: (formValues: FormValues) => void
}
interface FormValues {
username: string
password: string
}
function Form(props: FormProps) {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<form
onSubmit={e => {
e.preventDefault()
props.onSubmit({
username,
password
})
}}
>
<input
type="text"
placeholder="username"
onChange={e => {
setUsername(e.target.value)
}}
/>
<input
type="text"
placeholder="password"
onChange={e => {
setPassword(e.target.value)
}}
/>
<button type="submit">Submit</button>
</form>
)
}
render(<App />, document.getElementById('root'))
I assumed you are fresh in FE or React world. Propably you do not need that much-nested structure.
There is a special hook in React called useImperativeHandle. You can use it to call child's functions from parent.
You can find out more in the oficcial React documentation.
example of usage
Child Component
Needs to be wrapped into forwardRef like that:
export const ChildComponent = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
async functionName() {
await someLogic();
},
}));
Parent Component
In parent component you need to pass ref to the child.
Then you can use child's function this way:
const childRef = useRef(null)
childRef.current.functionName()

passing a variable from child component to parent component in Next.js

I have 2 components home and tiny tiny is imported inside home as u can see in the code
I am trying to pass value.toString("html") from tiny.js to home.js
if this is not possible at least help me integrate both tiny and home components as a single object so that I don't have to pass the value as props to a parent component
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [deatils1, setdeatils] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: deatils1,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny /> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
tiny.js
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Actually, you already have onChange event in tiny, so you only need to pass another onChange event from home to tiny.
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [details, setDetails] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: details,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny onChange={(value) => setDetails(value)}/> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}

Problem setting disabled prop and focus from the same function

I'm trying to build edit form fields. Here, you have to press a button before you can edit the form field. However, I'm having problems toggling the disabled prop and setting the focus of the element. This is some sample code. The input will only focus after I've clicked the button twice.
export default function App() {
const [isDisabled, setIsDisabled] = useState(true);
const inputEl = useRef(null);
const onBlur = () => {
setIsDisabled(true);
};
const handleEditClick = () => {
setIsDisabled(false);
inputEl.current.focus();
};
return (
<div className="App">
<h1>Focus problem</h1>
<h2>Focus only happens on the scond click!</h2>
<input ref={inputEl} onBlur={onBlur} disabled={isDisabled} />
<button onClick={() => handleEditClick()}>Can edit</button>
</div>
);
}
Here is a code-sandbox
Setting focus on a DOM element is a side-effect, it should be done within useEffect:
import React, { useState, useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const [isDisabled, setIsDisabled] = useState(true);
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, [isDisabled]);
const onBlur = () => {
setIsDisabled(true);
};
const handleEditClick = () => {
setIsDisabled(false);
};
return (
<div className="App">
<h1>Focus problem</h1>
<h2>Focus only happens on the scond click!</h2>
<input ref={inputEl} onBlur={onBlur} disabled={isDisabled} />
<button onClick={() => handleEditClick()}>Can edit</button>
</div>
);
}

react.js how can I autofocus a contentEditable element

How can I set focus on a contentEditable element in react?
I have found a lot pages anout the subjet, but cant get it to work.
Sample code:
import React, { useRef, useEffect } from 'react';
const About = () => {
const inputRef = useRef(null);
useEffect(inputRef => {
if (inputRef) {
inputRef.focus();
}
}, []);
return (
<div className="about">
<h2>About</h2>
<p className="paragraph"
contentEditable={true}
suppressContentEditableWarning={true}
spellCheck={false}
ref={inputRef}
>
Hello
</p>
</div >
)
}
export default About;
Looks like my solution is this.
import React, { useRef, useEffect } from 'react';
const About = () => {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, [inputRef]);
return (
<div className="about">
<h2>About</h2>
<p className="paragraph"
contentEditable={true}
suppressContentEditableWarning={true}
spellCheck={false}
ref={inputRef}
>
Hello
</p>
</div >
)
}
export default About;
May be you can use this:
<textarea className="paragraph"
ref={inputRef}
value={yourValueState}
autofocus/>
Found another working solution:
const inputRef = useCallback(node => {
node.focus()
}, [])

I can't get results from onChange on a React Typescript component

I'm using the React Color lib to create a custom component in my application. My child component is composed by two components of React Colors: CirclePicker and ChromePicker. Both share the colorPicker state variable as value. So, if one change his value, the other will change too.
As you can see on my Child Component, I put one invisible input that shares the same colorPicker value (I don't know if it's the right wat to do it). And my goal here is: when the input change, I could do something, for example: alert('COLOR CHANGED') (this is on my code in handleOnChange function)
Child component:
import React from 'react';
import { CirclePicker, ChromePicker } from 'react-color';
import { Icon } from './../Icon/Icon';
import './color-picker.scss';
export interface ColorPickerProps {
onChange: (value: string) => void;
}
export function ColorPicker(props: ColorPickerProps) {
const [colorPicker, showColorPicker] = React.useState(false);
const [finalColor, changeColor] = React.useState('#fff');
function handleOnChange() {
alert('COLOR CHANGED');
props.onChange(finalColor);
}
return (
<div className="relative-position">
<input type="text" value={finalColor} onChange={() => handleOnChange} style={{display: "none"}}/>
<CirclePicker
color={finalColor}
onChangeComplete={colore => changeColor(colore.hex)}
colors={[
'#004de8',
'#2ecc71',
'#ff9300',
'#62708b',
'#ff003a',
'#20396a'
]}
circleSize={24}
></CirclePicker>
<a
className="btn-select-color"
onClick={() => showColorPicker(!colorPicker)}
>
<Icon viewIcone="ArrowDropDown" style={{ margin: '5px' }} />
</a>
{colorPicker ? (
<span className="chrome-picker">
<ChromePicker
color={finalColor}
onChangeComplete={colore => changeColor(colore.hex)}
disableAlpha={true}
/>
</span>
) : (
''
)}
</div>
);
}
Situations:
I've tried more or less how they've explained here, but no matter what I do, the handleOnChange function is not called and I can't see the alert.
Furthermore, my goal is to use this component in parent, more or less like this:
**Parent component: **
<ColorPicker onChange={e => this.state.color = e} />
So, on this way, I could have on parent state the color picked.
I can't get nothing in the Child function, neither having the state updated on the parent component.
Could someone help me? I'm a new user of React :(
Explanation
The input's value is linked to state. So when state changes, the value changes. BUT, no event was fired on the input. You were trying to use a react event handler on an input that wasn't firing any events; meaning handleOnChange never got called so props.onChange never got called...
Solution
Use useEffect to listen for when the input's value/state value changes. If you use useRef, you can stop your props.onChange from running when the component mounts. Check out the DEMO.
import * as React from "react";
import { FunctionComponent, useState, useEffect, useRef } from "react";
import { render } from "react-dom";
import { CirclePicker, ChromePicker } from "react-color";
const colors = [
"#004de8",
"#2ecc71",
"#ff9300",
"#62708b",
"#ff003a",
"#20396a"
];
export interface ColorPickerProps {
onChange: (value: string) => void;
}
const ColorPicker: FunctionComponent<ColorPickerProps> = ({ onChange }) => {
const [colorPicker, showColorPicker] = useState(false);
const [finalColor, changeColor] = useState("#fff");
const componentMounted = useRef(true);
useEffect(() => {
if (componentMounted.current) {
componentMounted.current = false;
console.log(
"Don't run props.onChange when the component mounts with useRef"
);
} else {
onChange(finalColor);
alert("finalColor changed via useEffect");
}
return () => undefined;
}, [finalColor]);
return (
<div className="relative-position">
<input type="text" value={finalColor} style={{ display: "none" }} />
<CirclePicker
color={finalColor}
onChangeComplete={colore => changeColor(colore.hex)}
colors={colors}
circleSize={24}
/>
<br />
<button onClick={() => showColorPicker(!colorPicker)}>click me</button>
{colorPicker && (
<span className="chrome-picker">
<ChromePicker
color={finalColor}
onChangeComplete={colore => changeColor(colore.hex)}
disableAlpha={true}
/>
</span>
)}
</div>
);
};
const rootElement = document.getElementById("root");
render(
<ColorPicker
onChange={() => {
console.log("onChange");
}}
/>,
rootElement
);
useCallback may be a good option, refer to this QA, and document
import React from "react";
import ReactDOM from "react-dom";
import { CirclePicker, ChromePicker } from "react-color";
import "./styles.css";
function App(props) {
const [colorPicker, showColorPicker] = React.useState(false);
const [finalColor, changeColor] = React.useState("#fff");
const handleColorChange = React.useCallback(console.log("Work via callback"));
return (
<div className="relative-position">
<input value={finalColor} onChange={console.log("Work directly")} />
<input value={finalColor} onChange={handleColorChange} />
<CirclePicker
color={finalColor}
onChangeComplete={colore => changeColor(colore.hex)}
colors={[
"#004de8",
"#2ecc71",
"#ff9300",
"#62708b",
"#ff003a",
"#20396a"
]}
circleSize={24}
/>
{colorPicker ? (
<span className="chrome-picker">
<ChromePicker
color={finalColor}
onChangeComplete={colore => changeColor(colore.hex)}
disableAlpha={true}
/>
</span>
) : (
""
)}
<p>{finalColor}</p>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Resources