React - update state on props changed - input element - reactjs

I have already read all the stack-overflow questions related to this problem, also this official react post and the preferred solutions.
It's not recommended to use componentWillReceiveProps anymore!
Before you mark this question as duplicate, please understand my specific question, I didn't see any solution for my specific problem.
What I'm trying to do is very simple:
I have component KInputRange that received the value from props and send the value out (callback) onEnter event (will send the value to server only on enter)
The props.value can randomly change (coming by websocket from the server)
My Question:
Inside my components, the <input> value attribute will get the data from props or from state?
If from props:
How can I update the value internally when the user type input data?
If from state:
How can I update the new value if the props.value has change randomly from the server?
I'm actually need to update my internal state on props change
but how to do it today, if react says that's anti-pattern?
This my code so far:
class KInputRange extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
private onKeyDown(e: any): void {
//If the key is enter call to props.onEnter with the current value
}
private onChange(e: any): void {
//if user change the value- change the internal value
}
public render() {
return (
<input value={?????} type="text" onChange={(e) => this.onChange(e)} onKeyDown={(e) => this.onKeyDown(e)}/>
);
}
}
Usage:
<KInputRange value={this.state.dataFromTheServer} onEnter={(val: number) => this.kInputRangeEnterClicked(val)}/>

You can use a function component as mentioned in the post you linked here.
To update the value internally you can use React's State Hook.
Something like this:
import React, { useState } from 'react';
const KInputRange = (props) => {
const [value, setValue] = useState(props.value);
function onKeyDown(e: any): void {
//If the key is enter call to props.onEnter with the current value
}
function onChange(e: any): void {
setValue(e.target.value);
}
return (
<input value={value} type="text" onChange={(e) => this.onChange(e)} onKeyDown={(e) => this.onKeyDown(e)}/>
);
}

First, as #Atul said, you DO need to use getDerivedStateFromProps.
It's all because you need to control your component value depending on both - props and internal state.
Assuming you using flow this code should help:
// #flow
import * as React from "react";
type Properties = {
remoteValue: string,
onSubmit: (value: string) => void
};
type State = {
remoteValueMemo: string,
internalValue: string
};
class KInputRange extends React.Component<Properties, State> {
static defaultProps = {
remoteValue: "",
onSubmit: () => {}
};
state = {
remoteValueMemo: this.props.remoteValue,
internalValue: this.props.remoteValue
};
static getDerivedStateFromProps(props: Properties, state: State) {
if (state.remoteValueMemo !== props.remoteValue) {
return {
remoteValueMemo: props.remoteValue,
internalValue: props.remoteValue};
}
return null;
}
handleValueChange = (event: SyntheticEvent<HTMLInputElement>) => {
this.setState({internalValue: event.currentTarget.value});
};
handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
if (event.keyCode === 13) {
this.props.onSubmit(this.state.internalValue);
}
};
render(): React.Node {
const {internalValue} = this.state;
return (
<input value={internalValue} onChange={this.handleValueChange} onKeyDown={this.handleKeyDown}/>
);
}
}
export default KInputRange;

The argument passed to useState is not used to update the state on re-render.
You should use the useEffect Hook.
import React, { useEffect, useState } from 'react';
const Test = (props) => {
const [value, setValue] = React.useState({...props.value});
useEffect(() => {
setValue(props.value);
}, [props.value])
//...
}
Similar topic here: React.useState does not reload state from props

You can use useEffect hook of react whenever you need to do something on specific props change in function component
useEffect(() => {
// You can place your logic here to run whenever value changes
},[value]);
[value] is a dependency so that whenever the value changes you useEffect hook calls
Hope above is helpful to you.

Related

How to update a React component based on a context array value

Good morning,
I have been struggling with this Next.JS problem during the week, because we have a list of objects in a component that comes from the value of an array in a context.
Introduction
The issue here is:
That we have setSessionList that is a setter for a React state in the context.
sessionList that is an array of objects in the context
and the component InspectionLongList, that is the list itself, where all this objects are rendered and structured.
Question and doubts
Is it possible to conditionally update InspectionLongList when other component of the app updates a value of an object in the array sessionList, using setSessionList from the context?
Which is the good approach to do this?
Some code (for context)
The context:
import { createContext, useContext, useState } from "react";
interface IGobalState {
setSessionList: (value: any) => void;
sessionList: any;
}
export const AppContext = createContext({} as IGobalState);
export function AppWrapper({ children }: any) {
const [sessionList, setSessionList] = useState<any>([]);
return (
<AppContext.Provider
value={{
sessionList,
setSessionList,
}}
>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
And the consumer (wrapped in the Provider)
const {setSessionList, sessionList} = useAppContext();
useEffect(() => {
if (props.auth.isLoggedIn === true){
apiService.getAllInspections(props.auth.user.accessToken).then(res => setSessionList(res))
}
}, [sessionList, setSessionList])
UPDATE: How we consume the context for updates (InspectionLongList)
useEffect(() => {
if (props.auth.isLoggedIn === true){
apiService.getAllInspections(props.auth.user.accessToken).then(res => setSessionList(res))
}
}, [JSON.stringify(sessionList)]);
Thanks in advance,
Carles

Use state from a Context inside Class component

here is my Codepen which illustrates my current problem:
I woud like to use the class component, so I can call the forward function from parentComponents (through ref), but I currently cant figure out how to manipulate the context (Where the current state of the application is stored.
Can somebody help me ?
https://codesandbox.io/s/gallant-dust-vtp46?file=/src/App.tsx:0-1918
Kind regards
I don't know the exactly answer, but I have a solution, that you can forward function to parent component with Functional component. Check this code:
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
const ChildComponent(props, ref) => {
useImperativeHandle(ref, () => {
const funcCallFromParent = (params) => {
funcToHandle(params);
};
return { funcCallFromParent };
});
const doSomething(params) {
...
}
...
}
and then call if from your ParentComponent
...
childRef.current.funcCallFromParent(params);
...
This way will help you use Functional Component instead of Class Component, therefore easier to access the context.
Additional, maybe you'll want to try Redux, it's a good and most popular Context Management for ReactJS.
To consume React Context with class-based component you wrap them with the Consumer component and inject context value as props.
SStateButton
export class SStateButton extends Component {
refe;
name;
onclick;
constructor({
refe,
name,
onclick
}: {
refe: Status;
name: string;
onclick: any;
}) {
super({ refe, name, onclick });
this.refe = refe;
this.name = name;
this.onclick = onclick;
}
forwardToStatus = () => {
if (this.onclick) {
this.onclick(this.refe);
}
};
render() {
return (
<button
className="btn btn-light btn-outline-dark"
onClick={this.forwardToStatus}
>
ClassComponent {this.name}
</button>
);
}
}
App - Given context value value={[status, setStatus]}
<StateContext.Consumer>
{([, setStatus]) => (
<SStateButton
refe="1 Page"
name="Go to Page 1"
onclick={(val) => {
console.log("Additional functions added");
setStatus(val);
}}
/>
)}
</StateContext.Consumer>
Since SStateButton is being used in the same component that is providing the context value, and has the status state and updater function already in scope, the setStatus callback can also just be enclosed in the onclick callback and work just the same.
<SStateButton
refe="1 Page"
name="Go to Page 1"
onclick={(val) => {
console.log("Additional functions added");
setStatus(val);
}}
/>

Using react-hook-form in React with Typescript in child component

I am using a basic example of the react-hook-form library and even after looking up the documentary, I do not know how to pass the data from the form to another component. Here is my form component:
import { useForm, SubmitHandler } from "react-hook-form";
type FormInputs = {
minimalFrequency: number;
maximialFrequency: number;
};
// const onSubmit: SubmitHandler<FormInputs> = data => console.log(data);
export default function BasicUsage() {
const { register, formState: { errors }, handleSubmit, getValues } = useForm<FormInputs>({
defaultValues: {
min: 250,
max: 8000,
}
});
const onSubmit = (data: any) => {
console.log(data);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("minimalFrequency", { required: true })} />
{errors.minimalFrequency && "Only numbers are allowed"}
<input {...register("maximialFrequency", { required: true })} />
{errors.maximialFrequency && "Only numbers are allowed"}
<input type="submit" />
</form>
);
}
I would want to get the min and max values, in form of the given data object, of the user after they pushed the "submit" button and I just can't get my head around how it works.
My main component is a quite large class component, and I read that it might not work because react-hook-form needs a functional component. If true, is there a way to still use my class component somehow?
UPDATE: Added the parent component
import { useState } from "react";
import React from "react";
import BasicUsage from "./BasicUsage"
type Props = {
}
type State = {
dataFreq: object;
}
export default class Parent extends React.Component<Props, State>{
private timer: any;
constructor(props: Props) {
super(props);
this.state = {
dataFreq: {
minimalFrequency: 250,
maximialFrequency: 8000
}
};
}
getDataFromForm = (dataFreq: any) => {
this.setState({dataFreq: dataFreq })
console.log(dataFreq)
};
render() {
const minFreq = this.state.dataFreq;
console.log("This is a this dataFreq", this.state.dataFreq);
console.log("This is a this minimalFrequency", minFreq);
return (
<div>
<BasicUsage getDataFromForm={this.getDataFromForm}/>
</div>
);
}
}
You are still able to use your class component as a parent.
If I am I correct in assuming that you want to use data from the form in your main component, and the main component is the parent, you can define a function in your main component, something like
getDataFromForm(data){
this.setState({data: data })
}
Then you pass this function into your BasicUsage component
//In your main components render function, or wherever you are using the BasicUsage component
<BasicUsage
//other props you want to send into BasicUsage from the main component
getDataFromForm={getDataFromForm}
/>
Now in your BasicUsage component's onSubmit function you can call the function you passed as a prop as such
const onSubmit = (data: any) => {
//Do something with your data if you want to change format or process it somehow;
//in this case you should probably make a new variable and pass the new variable into getDataFromForm
props.getDataFromForm(data) //Call the function in the parent component
}
If you're using the form data in a sibling component and not a parent component, you would make the getDataFromForm function in a common parent and pass the function to the BasicUsage component and the state.data value into the sibling component where you want to access the data

React TypeScript: How to make a ref? [duplicate]

I'm using Typescript with React. I'm having trouble understanding how to use refs so as to get static typing and intellisense with respect to the react nodes referenced by the refs. My code is as follows.
import * as React from 'react';
interface AppState {
count: number;
}
interface AppProps {
steps: number;
}
interface AppRefs {
stepInput: HTMLInputElement;
}
export default class TestApp extends React.Component<AppProps, AppState> {
constructor(props: AppProps) {
super(props);
this.state = {
count: 0
};
}
incrementCounter() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div>
<h1>Hello World</h1>
<input type="text" ref="stepInput" />
<button onClick={() => this.incrementCounter()}>Increment</button>
Count : {this.state.count}
</div>
);
}}
If you’re using React 16.3+, the suggested way to create refs is using React.createRef().
class TestApp extends React.Component<AppProps, AppState> {
private stepInput: React.RefObject<HTMLInputElement>;
constructor(props) {
super(props);
this.stepInput = React.createRef();
}
render() {
return <input type="text" ref={this.stepInput} />;
}
}
When the component mounts, the ref attribute’s current property will be assigned to the referenced component/DOM element and assigned back to null when it unmounts. So, for example, you can access it using this.stepInput.current.
For more on RefObject, see #apieceofbart's answer or the PR createRef() was added in.
If you’re using an earlier version of React (<16.3) or need more fine-grained control over when refs are set and unset, you can use “callback refs”.
class TestApp extends React.Component<AppProps, AppState> {
private stepInput: HTMLInputElement;
constructor(props) {
super(props);
this.stepInput = null;
this.setStepInputRef = element => {
this.stepInput = element;
};
}
render() {
return <input type="text" ref={this.setStepInputRef} />
}
}
When the component mounts, React will call the ref callback with the DOM element, and will call it with null when it unmounts. So, for example, you can access it simply using this.stepInput.
By defining the ref callback as a bound method on the class as opposed to an inline function (as in a previous version of this answer), you can avoid the callback getting called twice during updates.
There used to be an API where the ref attribute was a string (see Akshar Patel's answer), but due to some issues, string refs are strongly discouraged and will eventually be removed.
Edited May 22, 2018 to add the new way of doing refs in React 16.3. Thanks #apieceofbart for pointing out that there was a new way.
React.createRef (class comp.)
class ClassApp extends React.Component {
inputRef = React.createRef<HTMLInputElement>();
render() {
return <input type="text" ref={this.inputRef} />
}
}
React.useRef (Hooks / function comp.)
a) Use readonly refs for React-managed DOM nodes:
const FunctionApp = () => {
// note the passed-in `null` arg ----------------v
const inputRef = React.useRef<HTMLInputElement>(null)
return <input type="text" ref={inputRef} />
}
inputRef.current becomes a readonly property by initializing its value with null.
b) Use mutable refs for arbitrary stored values akin to instance variables:
const FunctionApp = () => {
const renderCountRef = useRef(0)
useEffect(() => {
renderCountRef.current += 1
})
// ... other render code
}
Note: Don't initialize useRef with null in this case - it would make the renderCountRef type readonly (see example). If you need to provide null as initial value, do this:
const renderCountRef = useRef<number | null>(null)
Callback refs (both)
// Function component example, class analogue
const FunctionApp = () => {
const handleDomNodeChange = (domNode: HTMLInputElement | null) => {
// ... do something with changed dom node.
}
return <input type="text" ref={handleDomNodeChange} />
}
Note: String Refs are considered legacy and omitted for the scope of this answer.
Playground sample
One way (which I've been doing) is to setup manually :
refs: {
[string: string]: any;
stepInput:any;
}
then you can even wrap this up in a nicer getter function (e.g. here):
stepInput = (): HTMLInputElement => ReactDOM.findDOMNode(this.refs.stepInput);
Since React 16.3 the way to add refs is to use React.createRef as Jeff Bowen pointed in his answer. However you can take advantage of Typescript to better type your ref.
In your example you're using ref on input element. So they way I would do it is:
class SomeComponent extends React.Component<IProps, IState> {
private inputRef: React.RefObject<HTMLInputElement>;
constructor() {
...
this.inputRef = React.createRef();
}
...
render() {
<input type="text" ref={this.inputRef} />;
}
}
By doing this when you want to make use of that ref you have access to all input methods:
someMethod() {
this.inputRef.current.focus(); // 'current' is input node, autocompletion, yay!
}
You can use it on custom components as well:
private componentRef: React.RefObject<React.Component<IProps>>;
and then have, for example, access to props :
this.componentRef.current.props; // 'props' satisfy IProps interface
If you're using React.FC, add the HTMLDivElement interface:
const myRef = React.useRef<HTMLDivElement>(null);
And use it like the following:
return <div ref={myRef} />;
EDIT: This is no longer the right way to use refs with Typescript. Look at Jeff Bowen's answer and upvote it to increase its visibility.
Found the answer to the problem. Use refs as below inside the class.
refs: {
[key: string]: (Element);
stepInput: (HTMLInputElement);
}
Thanks #basarat for pointing in the right direction.
For those looking on how to do it when you have an array of elements:
const textInputRefs = useRef<(HTMLDivElement | null)[]>([])
...
const onClickFocus = (event: React.BaseSyntheticEvent, index: number) => {
textInputRefs.current[index]?.focus()
};
...
{items.map((item, index) => (
<textInput
inputRef={(ref) => textInputs.current[index] = ref}
/>
<Button
onClick={event => onClickFocus(event, index)}
/>
}
To use the callback style (https://facebook.github.io/react/docs/refs-and-the-dom.html) as recommended on React's documentation you can add a definition for a property on the class:
export class Foo extends React.Component<{}, {}> {
// You don't need to use 'references' as the name
references: {
// If you are using other components be more specific than HTMLInputElement
myRef: HTMLInputElement;
} = {
myRef: null
}
...
myFunction() {
// Use like this
this.references.myRef.focus();
}
...
render() {
return(<input ref={(i: any) => { this.references.myRef = i; }}/>)
}
For typescript user no constructor required.
...
private divRef: HTMLDivElement | null = null
getDivRef = (ref: HTMLDivElement | null): void => {
this.divRef = ref
}
render() {
return <div ref={this.getDivRef} />
}
...
If you wont to forward your ref, in Props interface you need to use RefObject<CmpType> type from import React, { RefObject } from 'react';
Lacking a complete example, here is my little test script for getting user input when working with React and TypeScript. Based partially on the other comments and this link https://medium.com/#basarat/strongly-typed-refs-for-react-typescript-9a07419f807#.cdrghertm
/// <reference path="typings/react/react-global.d.ts" />
// Init our code using jquery on document ready
$(function () {
ReactDOM.render(<ServerTime />, document.getElementById("reactTest"));
});
interface IServerTimeProps {
}
interface IServerTimeState {
time: string;
}
interface IServerTimeInputs {
userFormat?: HTMLInputElement;
}
class ServerTime extends React.Component<IServerTimeProps, IServerTimeState> {
inputs: IServerTimeInputs = {};
constructor() {
super();
this.state = { time: "unknown" }
}
render() {
return (
<div>
<div>Server time: { this.state.time }</div>
<input type="text" ref={ a => this.inputs.userFormat = a } defaultValue="s" ></input>
<button onClick={ this._buttonClick.bind(this) }>GetTime</button>
</div>
);
}
// Update state with value from server
_buttonClick(): void {
alert(`Format:${this.inputs.userFormat.value}`);
// This part requires a listening web server to work, but alert shows the user input
jQuery.ajax({
method: "POST",
data: { format: this.inputs.userFormat.value },
url: "/Home/ServerTime",
success: (result) => {
this.setState({ time : result });
}
});
}
}
From React type definition
type ReactInstance = Component<any, any> | Element;
....
refs: {
[key: string]: ReactInstance
};
So you can access you refs element as follow
stepInput = () => ReactDOM.findDOMNode(this.refs['stepInput']);
without redefinition of refs index.
As #manakor mentioned you can get error like
Property 'stepInput' does not exist on type '{ [key: string]:
Component | Element; }
if you redefine refs(depends on IDE and ts version you use)
Just to add a different approach - you can simply cast your ref, something like:
let myInputElement: Element = this.refs["myInput"] as Element
I always do this, in that case
to grab a ref
let input: HTMLInputElement = ReactDOM.findDOMNode<HTMLInputElement>(this.refs.input);
FIRST ADD AN IMPORT
import React, { useRef } from "react";
THEN THIS
const studentCapacityRef = useRef<HTMLInputElement>(null);
OR THIS
const studentCapacityRef = useRef<HTMLAreaElement>(null);
OR THIS
const studentCapacityRef = useRef<HTMLDivElement>(null);
etc...
class SelfFocusingInput extends React.Component<{ value: string, onChange: (value: string) => any }, {}>{
ctrls: {
input?: HTMLInputElement;
} = {};
render() {
return (
<input
ref={(input) => this.ctrls.input = input}
value={this.props.value}
onChange={(e) => { this.props.onChange(this.ctrls.input.value) } }
/>
);
}
componentDidMount() {
this.ctrls.input.focus();
}
}
put them in an object

How to use refs in React with Typescript

I'm using Typescript with React. I'm having trouble understanding how to use refs so as to get static typing and intellisense with respect to the react nodes referenced by the refs. My code is as follows.
import * as React from 'react';
interface AppState {
count: number;
}
interface AppProps {
steps: number;
}
interface AppRefs {
stepInput: HTMLInputElement;
}
export default class TestApp extends React.Component<AppProps, AppState> {
constructor(props: AppProps) {
super(props);
this.state = {
count: 0
};
}
incrementCounter() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div>
<h1>Hello World</h1>
<input type="text" ref="stepInput" />
<button onClick={() => this.incrementCounter()}>Increment</button>
Count : {this.state.count}
</div>
);
}}
If you’re using React 16.3+, the suggested way to create refs is using React.createRef().
class TestApp extends React.Component<AppProps, AppState> {
private stepInput: React.RefObject<HTMLInputElement>;
constructor(props) {
super(props);
this.stepInput = React.createRef();
}
render() {
return <input type="text" ref={this.stepInput} />;
}
}
When the component mounts, the ref attribute’s current property will be assigned to the referenced component/DOM element and assigned back to null when it unmounts. So, for example, you can access it using this.stepInput.current.
For more on RefObject, see #apieceofbart's answer or the PR createRef() was added in.
If you’re using an earlier version of React (<16.3) or need more fine-grained control over when refs are set and unset, you can use “callback refs”.
class TestApp extends React.Component<AppProps, AppState> {
private stepInput: HTMLInputElement;
constructor(props) {
super(props);
this.stepInput = null;
this.setStepInputRef = element => {
this.stepInput = element;
};
}
render() {
return <input type="text" ref={this.setStepInputRef} />
}
}
When the component mounts, React will call the ref callback with the DOM element, and will call it with null when it unmounts. So, for example, you can access it simply using this.stepInput.
By defining the ref callback as a bound method on the class as opposed to an inline function (as in a previous version of this answer), you can avoid the callback getting called twice during updates.
There used to be an API where the ref attribute was a string (see Akshar Patel's answer), but due to some issues, string refs are strongly discouraged and will eventually be removed.
Edited May 22, 2018 to add the new way of doing refs in React 16.3. Thanks #apieceofbart for pointing out that there was a new way.
React.createRef (class comp.)
class ClassApp extends React.Component {
inputRef = React.createRef<HTMLInputElement>();
render() {
return <input type="text" ref={this.inputRef} />
}
}
React.useRef (Hooks / function comp.)
a) Use readonly refs for React-managed DOM nodes:
const FunctionApp = () => {
// note the passed-in `null` arg ----------------v
const inputRef = React.useRef<HTMLInputElement>(null)
return <input type="text" ref={inputRef} />
}
inputRef.current becomes a readonly property by initializing its value with null.
b) Use mutable refs for arbitrary stored values akin to instance variables:
const FunctionApp = () => {
const renderCountRef = useRef(0)
useEffect(() => {
renderCountRef.current += 1
})
// ... other render code
}
Note: Don't initialize useRef with null in this case - it would make the renderCountRef type readonly (see example). If you need to provide null as initial value, do this:
const renderCountRef = useRef<number | null>(null)
Callback refs (both)
// Function component example, class analogue
const FunctionApp = () => {
const handleDomNodeChange = (domNode: HTMLInputElement | null) => {
// ... do something with changed dom node.
}
return <input type="text" ref={handleDomNodeChange} />
}
Note: String Refs are considered legacy and omitted for the scope of this answer.
Playground sample
One way (which I've been doing) is to setup manually :
refs: {
[string: string]: any;
stepInput:any;
}
then you can even wrap this up in a nicer getter function (e.g. here):
stepInput = (): HTMLInputElement => ReactDOM.findDOMNode(this.refs.stepInput);
Since React 16.3 the way to add refs is to use React.createRef as Jeff Bowen pointed in his answer. However you can take advantage of Typescript to better type your ref.
In your example you're using ref on input element. So they way I would do it is:
class SomeComponent extends React.Component<IProps, IState> {
private inputRef: React.RefObject<HTMLInputElement>;
constructor() {
...
this.inputRef = React.createRef();
}
...
render() {
<input type="text" ref={this.inputRef} />;
}
}
By doing this when you want to make use of that ref you have access to all input methods:
someMethod() {
this.inputRef.current.focus(); // 'current' is input node, autocompletion, yay!
}
You can use it on custom components as well:
private componentRef: React.RefObject<React.Component<IProps>>;
and then have, for example, access to props :
this.componentRef.current.props; // 'props' satisfy IProps interface
If you're using React.FC, add the HTMLDivElement interface:
const myRef = React.useRef<HTMLDivElement>(null);
And use it like the following:
return <div ref={myRef} />;
EDIT: This is no longer the right way to use refs with Typescript. Look at Jeff Bowen's answer and upvote it to increase its visibility.
Found the answer to the problem. Use refs as below inside the class.
refs: {
[key: string]: (Element);
stepInput: (HTMLInputElement);
}
Thanks #basarat for pointing in the right direction.
For those looking on how to do it when you have an array of elements:
const textInputRefs = useRef<(HTMLDivElement | null)[]>([])
...
const onClickFocus = (event: React.BaseSyntheticEvent, index: number) => {
textInputRefs.current[index]?.focus()
};
...
{items.map((item, index) => (
<textInput
inputRef={(ref) => textInputs.current[index] = ref}
/>
<Button
onClick={event => onClickFocus(event, index)}
/>
}
To use the callback style (https://facebook.github.io/react/docs/refs-and-the-dom.html) as recommended on React's documentation you can add a definition for a property on the class:
export class Foo extends React.Component<{}, {}> {
// You don't need to use 'references' as the name
references: {
// If you are using other components be more specific than HTMLInputElement
myRef: HTMLInputElement;
} = {
myRef: null
}
...
myFunction() {
// Use like this
this.references.myRef.focus();
}
...
render() {
return(<input ref={(i: any) => { this.references.myRef = i; }}/>)
}
For typescript user no constructor required.
...
private divRef: HTMLDivElement | null = null
getDivRef = (ref: HTMLDivElement | null): void => {
this.divRef = ref
}
render() {
return <div ref={this.getDivRef} />
}
...
If you wont to forward your ref, in Props interface you need to use RefObject<CmpType> type from import React, { RefObject } from 'react';
Lacking a complete example, here is my little test script for getting user input when working with React and TypeScript. Based partially on the other comments and this link https://medium.com/#basarat/strongly-typed-refs-for-react-typescript-9a07419f807#.cdrghertm
/// <reference path="typings/react/react-global.d.ts" />
// Init our code using jquery on document ready
$(function () {
ReactDOM.render(<ServerTime />, document.getElementById("reactTest"));
});
interface IServerTimeProps {
}
interface IServerTimeState {
time: string;
}
interface IServerTimeInputs {
userFormat?: HTMLInputElement;
}
class ServerTime extends React.Component<IServerTimeProps, IServerTimeState> {
inputs: IServerTimeInputs = {};
constructor() {
super();
this.state = { time: "unknown" }
}
render() {
return (
<div>
<div>Server time: { this.state.time }</div>
<input type="text" ref={ a => this.inputs.userFormat = a } defaultValue="s" ></input>
<button onClick={ this._buttonClick.bind(this) }>GetTime</button>
</div>
);
}
// Update state with value from server
_buttonClick(): void {
alert(`Format:${this.inputs.userFormat.value}`);
// This part requires a listening web server to work, but alert shows the user input
jQuery.ajax({
method: "POST",
data: { format: this.inputs.userFormat.value },
url: "/Home/ServerTime",
success: (result) => {
this.setState({ time : result });
}
});
}
}
From React type definition
type ReactInstance = Component<any, any> | Element;
....
refs: {
[key: string]: ReactInstance
};
So you can access you refs element as follow
stepInput = () => ReactDOM.findDOMNode(this.refs['stepInput']);
without redefinition of refs index.
As #manakor mentioned you can get error like
Property 'stepInput' does not exist on type '{ [key: string]:
Component | Element; }
if you redefine refs(depends on IDE and ts version you use)
Just to add a different approach - you can simply cast your ref, something like:
let myInputElement: Element = this.refs["myInput"] as Element
I always do this, in that case
to grab a ref
let input: HTMLInputElement = ReactDOM.findDOMNode<HTMLInputElement>(this.refs.input);
FIRST ADD AN IMPORT
import React, { useRef } from "react";
THEN THIS
const studentCapacityRef = useRef<HTMLInputElement>(null);
OR THIS
const studentCapacityRef = useRef<HTMLAreaElement>(null);
OR THIS
const studentCapacityRef = useRef<HTMLDivElement>(null);
etc...
class SelfFocusingInput extends React.Component<{ value: string, onChange: (value: string) => any }, {}>{
ctrls: {
input?: HTMLInputElement;
} = {};
render() {
return (
<input
ref={(input) => this.ctrls.input = input}
value={this.props.value}
onChange={(e) => { this.props.onChange(this.ctrls.input.value) } }
/>
);
}
componentDidMount() {
this.ctrls.input.focus();
}
}
put them in an object

Resources