Can't use ref in React component - javascript

I created a react component with an input and wanted to pass a ref and some other props to it, but it gives an error I created a react component with an input and wanted to pass a ref and some other props to it, but it gives an error
Type '{ ref: RefObject; type: string; placeholder: string; }' is not assignable to type 'IntrinsicAttributes & RefAttributes'.
Property 'type' does not exist on type 'IntrinsicAttributes & RefAttributes'.
code:
...
MyInput.jsx
...
import React, { forwardRef } from 'react'
import classes from './MyInput.module.css'
const MyInput = forwardRef((props, ref) => {
return (
<input ref={ref} className={classes.myInput} {...props} />
);
});
export default MyInput;
...
App.js
...
const bodyInputRef = React.createRef()
...
<MyInput
ref={bodyInputRef}
type="text"
placeholder='Post body
/>

Related

bootstrap form control props rest type

I am creating my own input component over react-bootstrap.
I have done something like this
import Form from "react-bootstrap/Form";
import InputGroup from "react-bootstrap/InputGroup";
import React, { InputHTMLAttributes } from "react";
import { FormControlProps } from "react-bootstrap/esm/FormControl";
export interface Props extends InputHTMLAttributes<HTMLInputElement> {
value: string;
addonLeft?: string;
addonRight?: string;
rest?: FormControlProps;
}
//NOTE: This is only for illustration purpose.
// ideally input component would've more stuff like label
const Input: React.FC<Props> = ({ addonLeft, addonRight, ...rest }) => {
return (
<InputGroup className="mb-3">
{addonLeft && <InputGroup.Text>{addonLeft}</InputGroup.Text>}
<Form.Control
{...rest}
/>
</InputGroup>
);
};
export default Input;
But it highlights <Form.Control> and says this
Types of property 'size' are incompatible.
Type 'number | undefined' is not assignable to type '"sm" | "lg" | undefined'.
Type 'number' is not assignable to type '"sm" | "lg" | undefined'.
```

What type is register from react-hook-form?

When using react-hook-form with Typescript, there is a component that sends some props, register being one of them.
The issue is with its type when declared in an interface:
export interface MyProps {
title: string;
...
register: (string | undefined) => void;
}
Which is the correct way of declaring register here?
Also tried with:
import { RegisterOptions } from 'react-hook-form';
export interface MyProps {
title: string;
...
register: RegisterOptions;
}
if you are calling register as a prop in a custom component or to use in some custom input field in Typescript, then you can use the below code.
First of all import the UseFormRegister and FieldValues from 'react-hook-form'
import {UseFormRegister, FieldValues } from 'react-hook-form'
After that, define the type of register as
register: UseFormRegister<FieldValues>
and boom, you don't need to use 'any' type for register.
Try this.
import { RegisterOptions, UseFormRegisterReturn } from 'react-hook-form';
export interface MyProps {
title: string;
...
register: (name: string, options?: RegisterOptions) => UseFormRegisterReturn;
}
You can retrieve register type from UseFormReturn.
import { UseFormReturn } from 'react-hook-form';
export interface MyProps {
title: string;
register: UseFormReturn['register'];
}
At react-hook-form, we recommend to forward ref instead of passing register method.
You can choose "forwardRef" or "useFormContext"
Try this!
// Input.tsx
interface InputProps
extends React.PropsWithRef<JSX.IntrinsicElements['input']> {
id: string;
label?: string | ReactElement;
flex?: boolean;
}
const Input = forwardRef<HTMLInputElement, InputProps>(
({ id, label, flex = false, ...rest }, ref) => {
return (
<InputWrap flex={flex}>
{label && (
<InputLabel flex={flex} htmlFor={id}>
{label}
</InputLabel>
)}
<InputField id={id} ref={ref} {...rest} />
</InputWrap>
);
},
);
// form.tsx
<Input
label="password"
id="password"
type="password"
autoComplete="new-password"
register('password')}
/>

How to type React Component whose props get partially applied?

I have a function that takes a React component and partially applies its props. This is used e.g. to supply a component with a theme from a consumer.
It basically converts this <FancyComponent theme="black" text="blah"/> to this <AlreadyFanciedComponent text="blah"/>, which is the same component just with the theme already provided.
I cannot work out how this has to be typed.
Here is some working code throwing TypeScript errors:
import * as React from "react";
import * as ReactDOM from "react-dom";
import { ComponentType } from "react";
type Theme = {theme: string};
function preTheme<TProps extends Theme>(WrappedComponent: ComponentType<TProps>) {
return React.forwardRef<typeof WrappedComponent, TProps>((props: TProps, ref) => {
// here could be a Consumer
return <WrappedComponent theme={props.theme} ref={ref} {...props} />
})
}
const ThemedP: React.FC<{theme: string, text: string}> = props => <p className={props.theme}>{props.text}</p>;
const PreThemedP = preTheme(ThemedP);
ReactDOM.render(<PreThemedP text={"Here be useless text"}/>, document.getElementById('root'));
The error is: Property 'theme' is missing in type '{ text: string; }' but required in type '{ theme: string; text: string; }'
Here it is in a playground.
You need to Omit the theme property from the props of the wrapped component:
return React.forwardRef<typeof WrappedComponent, Omit<TProps, "theme">>(...);

How to pass state to child component with function based React?

I try to pass uploaded file to the child component that both parent and child components are the function based.
I am using React, TypeScript and Material-UI
Parents
import React from 'react';
import Child from './Child';
const Parent:React.FC =() => {
const [file, setFile] = React.useState(null);
const onChangeFile = (e:any) => {
setFile(e!.target.files[0]);
}
return(
<div>
<input
accept="*.*"
onChange={onChangeFile}
id="uploadFile"
type="file" />
<Button
onClick={() => document.getElementById('uploadFile')!.click()}
component='span'>
Upload file
</Button>
<Child files={file: File}>
</div>
Child
import React from 'react';
const Child:React.FC = (props) => {
return(
<div>
{{ props }}
</div>
)
}
export default Child
At this point, I just want to print out the file's information that I have {{ props }}. I am a newbie to React and please tell me anything I did wrong. Thanks in advance!
EDIT
I changed Child,
const Child:React.FC = ({files}) => {
return(
<div>
{ files }
</div>
)
}
then it throws error,
property 'files' does not exist on type '{children?:ReactNode; }'
EDIT2
Following to #EarlePoole, I changed my code.
Parents
const Parent:React.FC = () => {
//...
<Child file={file} />
Child
const Child:React.FC =(props) => {
return(
<div>
{props.file.file}
</div>
)
}
In parent component, I got this error
Type '{ file: any; } is not assignable to type 'IntrinsicAttributes & {children ?:ReactNode; }'. Property 'file' does not exist on type 'IntrinsicAttributes & { children?: ReactNode}'
In Child component,
Property 'file' does not exist on type '{children?:ReactNode; }'
While you did provide the typings for your functional component, you did not specify the generic type for React.FC.
You should define an interface for the props of your functional component, and supply it as the generic type.
interface ChildProps {
files: any; // try not to use any.
}
const Child:React.FC<ChildProps> = ({files}) => {
return(
<div>
{ files }
</div>
)
}
On my example, I used any, as I am unsure the exact type of files. You should avoid using any, and replace it with the respective type.
In your child, if you put {{props}} in your div to be printed out, it's going to likely give you [object Object] or something, because that is what props is.
In your parent you have this code <Child files={file: File}>. This assigns your {file: File} object to props.files in your Child component, which can then be accessed, from within your Child component, by doing props.files.file which will return the File item.
To avoid confusion and redundancy I would recommend you change your Child props assignment in your Parent component to <Child file={file}>.

Why my code is returning React Type Error?

When I am passing props to child element I get the following error:
TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & FooterRightSideProps & { children?: ReactNode; }'.
Type '{}' is not assignable to type 'FooterRightSideProps'.
Property 'onClickCreate' is missing in type '{}'.
My code is like below:
import React from "react";
import CSSModules from "react-css-modules";
import styles from "./Footer.module.sass";
import { Icon } from "#components/icon/Icon";
import { Link } from "#components/typography/link";
import { Button } from "#components/button/Button";
export interface FooterProps {
}
export const Footer: React.SFC<FooterProps> =
CSSModules(styles)
(
(props: FooterProps) =>
<div styleName="footer">
<FooterLeftSide />
<FooterRightSide { ...props } /> //an error occurs here
</div>
);
export const FooterLeftSide: React.SFC =
CSSModules(styles)(
() =>
<div styleName="footer-left-side"></div>
);
export interface FooterRightSideProps {
onClickAbandon?: () => void;
onClickCreate: () => void;
}
export const FooterRightSide: React.SFC<FooterRightSideProps> =
CSSModules(styles)
(
(props: FooterRightSideProps) =>
<div styleName="footer-right-side">
<Link
className="option-back"
styleName="header-option"
onClick={props.onClickAbandon}
>
<div styleName="icon-with-label">
<Icon name="left" />
</div>
Abandon
</Link>
<Button
onClick={props.onClickCreate}
theme="primary-white"
>
Create Profile
</Button>
</div>
);
Have any of you got an idea, how can I pass this onClickCreate prop to my child element nested in the parent?
props that You are passing to FooterRightSide are of type FooterProps, but this component expects props that satisfy FooterRightSideProps.
export interface FooterProps extends FooterRightSideProps {} and pass into Footer as a prop, either manually <Footer onClickCreate={someCallbackReference} onClickAbandon={optionalSomeCallbackReference} /> or more probably from binding to a flux/redux store via an HOC, eg for redux...
import {connect} from 'react-redux'
import {Dispatch, bindActionCreators} from 'redux'
import * as someActions from './someActions'
import {YourApplicationStateShape} from './someStateInterfaces'
// your Footer definitions here...
// modify FooterProps in that to be...
export interface FooterProps extends FooterRightSideProps {}
export default connect(
null, // no state props need mapping in this particular case
(dispatch: Dispatch<YourApplicationStateShape>): FooterProps =>
bindActionCreators({
onClickCreate: () => someActions.createSomething()
onClickAbandon: () => someActions.abandonSomething()
}, dispatch)
)(Footer)

Categories

Resources