How to get params and type definition for custom React/React-Native components - reactjs

I built a custom React Native Input Component which shows a dark border at the bottom when focused but when I am using this component then I am not getting props suggestion provided by VS Code Intellisense.
function TextInputComponent(props) {
const [controlledValue, setControlledValue] = useState('');
const [focused, setFocused] = useState(false);
const {
placeholder,
onChangeText,
autoCapitalize,
ref,
secureTextEntry,
value,
defaultValue,
controlled,
editable,
...rest
} = props;
return (
<TextInput
ref={ref}
value={controlled ? controlledValue : value}
defaultValue={defaultValue}
secureTextEntry={secureTextEntry}
placeholder={placeholder}
placeholderTextColor={globalColors.semiGray}
autoCapitalize={autoCapitalize}
editable={editable}
mode="outlined"
onFocus={() => {
setFocused(true);
}}
onBlur={() => {
setFocused(false);
}}
style={[
styles.textInput,
focused ? {borderColor: globalColors.blue} : {},
]}
onChangeText={text => {
if (controlled) {
console.log(text.toLowerCase());
setControlledValue(text.toLowerCase());
onChangeText(text);
} else {
onChangeText(text);
}
}}
{...rest}
/>
);
}
const styles = StyleSheet.create({
textInput: {
...elementStyles.input,
},
});
Is there any way I can get prop and type definitions without using TypeScript?
/**
*
* #param {{header: string, subheader: string, imageAlt: string, contentList: Array, orderLink: Object, contentLink: Object}} props
*/
I have seen #params way of definition but I just want to extend the default TextInput Definition not to write all the type definitions again for my custom component.

check Prop-Types . you can easily do prop checking with it

Related

How to get floatValue automatically from react-number-format inside a antd Form.Item?

I'm looking for a way to antd Form get automatically a floatValue from react-number-format.
`<Form.Item
label="My label"
name="fator"
>
<NumericFormat
thousandSeparator="."
decimalSeparator=","
decimalScale={2}
prefix="R$"
fixedDecimalScale={true}
onValueChange={(values, sourceInfo) => {
setState({
...state,
fator: values.floatValue!,
});
}}
/>
</Form.Item>`
Some people used onValueChange to get floatValue, but I do not know if it is the only way or the best way to get that value. On that way, we have to check each field to assign the correct value.
In my opinion, it is not the best way. It is so manually work.
const submit = async (values: stateModel) => {
values.fator = state.fator;
...
...
await doRegister(values);
..
}
How the best way to solve the problem?
I tried using some replacement on values returned from form submit, but it is not the best way to solve the problem.
You can solve this issue by creating a custom Number Field, which takes value and onChange as props.
const CustomNumberField = ({ value, onChange }) => {
return (
<NumericFormat
value={value}
thousandSeparator='.'
decimalSeparator=','
decimalScale={2}
prefix='R$'
fixedDecimalScale={true}
onValueChange={(values, sourceInfo) => {
onChange(values.floatValue);
}}
/>
);
};
Now the main Form will look like this:
const App = () => {
return (
<Form
onFinish={(values) => {
console.log(values); // { factor: 12 }
}}
>
<Form.Item label='My label' name='fator'>
<CustomNumberField />
</Form.Item>
{/* OR */}
{/* <Form.Item name='field'>
{(control, meta, form) => {
return <CustomNumberField {...control} />;
}}
</Form.Item> */}
<Button htmlType='submit'>Submit</Button>
</Form>
);
};
Q: Where that value & onChange prop come from?
When you pass name prop to Form.Item, the field inside the Form.Item is now controlled by Form. You can either pass a ReactElement or a function. For ReactElement, it pass two props value & onChange. For callback function, it looks like this:
children?: React.ReactElement | ((control: ChildProps, meta: Meta, form: FormInstance<Values>) => React.ReactNode);
interface ChildProps {
[name: string]: any;
}
interface Meta {
touched: boolean;
validating: boolean;
errors: string[];
warnings: string[];
name: InternalNamePath;
}
type InternalNamePath = (string | number)[];

React hook form pass an array of values to child - some Regster and some not

I want to pass an array of strings to my Child Component which creates an input field managed by React Hook Form.
Sometimes, I want the React Hook Form to NOT register some fields in the array passed as the input fields are optional to the user. For example, address_line2 in the inputForm array is not always required to be completed on the form.
I use this Child Component in a number of places, so if I change it, is it possible to make the change or component optional or use an if statement? So, that i can continue to use the child component as is.
Here is the parent component:
const StopForm: React.FC<IProps> = ({ id, index, stop }) => {
const Router = useRouter();
const {
register,
handleSubmit,
unregister,
formState: { errors },
} = useForm<Stop>({
defaultValues: sampleStopData,
});
const inputFields = [
"address_line1",
"address_line2",
];
return (
<>
{inputFields.map((field, index) => (
<InputField
key={index}
val={field}
register={register}
defaultValue={
typeof stop === "object" &&
typeof stop[field as keyof Stop] !== "undefined"
? // ? stop[field as keyof Stop].toString()
String(stop[field as keyof Stop])
: ""
}
errors={errors}
classes="px-3 py-1.5 rounded placeholder:text-gray-800 lg:w-[25rem] lg:mx-auto bg-transparent border outline-none"
/>
))}
</>);
My Child Component that manages the react hook form part is this:
interface IProps {
val: string;
type?: string;
defaultValue: string;
register: UseFormRegister<any>;
errors: any;
classes: string;
}
const InputField: React.FC<IProps> = ({
defaultValue,
register,
errors,
val,
classes,
type = "text",
}) => {
return (
<>
<input
id={val}
type={type}
placeholder={val}
className={`${classes} ${
errors?.val ? "border-red-900" : "border-gray-400"
}`}
defaultValue={defaultValue}
{...register(val, {
required: { value: true, message: `${val} is required` },
})}
/>
{errors[val] && (
<small className="text-red-700">{errors[val].message}</small>
)}
</>
);
};
export default InputField;
Can the input fields also pass a value or somehow let the child component know to 'skip' that value and not register it?
Any assistance on how to do this - I have been going round and round on this one, wouuld be graciously received!
Thank you!
Karen

How to test functionality of function props in storybook?

I have a parent component, <AssetSelectorMenu>, which is composed of two child components:
export const AssetSelectorMenu = (({ assets, sortByName }) => {
return (
<>
<AssetSelectorHeader sortByName={sortByName} />
{assets && assets.map((asset) => (
<AssetSelectorRow key={asset} />
))}
</>
);
});
storybook for AssetSelectorMenu:
export const Default = () => (
<AssetSelectorMenu sortByName={action("sortByName")} assets={assets} />
);
Inside the storybook for AssetSelectorMenu, I'd like to test that the function prop sortByName actually visually sorts the assets by name. At the moment, it only makes sure it the function gets called, but visually it's not sorting the assets. How can I do that?
If you want to use state in your Storybook examples so that your components are fully working based on interaction you need to use the createElement function from React.
Here is a simple example using a Checkbox component that has it's value managed by state which simulates using a state manager like Redux or Context etc.
import { Fragment, useState, createElement } from 'react'
<Preview>
<Story name="Default">
{createElement(() => {
const [value, setValue] = useState(['Yes'])
const onChange = (event, value) => {
setValue(value)
}
return (
<Checkbox
name="checkbox"
values={[
{ label: 'Yes', value: 'Yes' },
{ label: 'No', value: 'No' }
]}
value={value}
onChange={onChange}
/>
)
})}
</Story>
</Preview>

React component won't take onChange event

Obligatory "new to react" paragraph here. I have this rating component I got from material-ui and i'm trying to send the value to a database.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Rating from '#material-ui/lab/Rating';
import Box from '#material-ui/core/Box';
const labels = {
0.5: 'Worst of the Worst',
1: 'Bad',
1.5: 'Poor',
2: 'Passable',
2.5: 'Ok',
3: 'Good',
3.5: 'Damn Good',
4: 'Great',
4.5: 'Love',
5: 'Perfection',
};
const useStyles = makeStyles({
root: {
width: 200,
display: 'flex',
alignItems: 'center',
},
});
export default function HoverRating(props) {
const [value, setValue] = React.useState(2);
const [hover, setHover] = React.useState(-1);
const classes = useStyles();
const onRatingChange = (event) => {
console.log(event.target.value)
props.reduxDispatch ({ type: "RATING_CHANGE", value: event.target.value
})
}
return (
<div className={classes.root}>
<Rating
name="hover-feedback"
value={value}
defaultValue={0}
precision={0.5}
size="large"
onChange={(event, newValue) => {
setValue(newValue);
console.log("your newValue is " + newValue)
}}
onChangeActive={(event, newHover) => {
setHover(newHover);
}}
{ onRatingChange }
/>
<br/>
{value !== null && <Box ml={2}>{labels[hover !== -1 ? hover : value]}</Box>}
</div>
);
}
It doesn't like something about my onRatingChange function. I've moved it all over the place and it's still throwing errors. I just really don't understand the issue. I'm mostly getting-
"./src/components/Rating.js
Line 54:11: Parsing error: Unexpected token, expected "..."
I've been at this for hours and I salvation.
Change your code from:
{ onRatingChange }
to:
onRatingChange={onRatingChange}
and change your file extension from .js to .jsx because you are using the JSX syntax
First, you appear the be storing the rating in two places: in your local state (with React.useState), and from the looks of your onRatingChange function, in a Redux store somewhere. It would be a good idea to pick one, and use that.
As for the direct answer to your question, your syntax is wrong. You're writing your Rating component in the following way:
<Rating
// ...
onChange={(event, newValue) => {
setValue(newValue);
console.log("your newValue is " + newValue)
}}
// ...
{ onRatingChange }
/>
The Rating component expects an onChange prop. I assume you want your onRatingChange function to be called when the rating changes. As such, you'd write:
<Rating
// ...
onChange={onRatingChange}
/>
The complication here though is that you're trying to register two different handlers for the rating change event. The bottom line is, decide on one, and then pass that as a callback function to the onChange prop.

Keyboard dismisses while typing TextInput in nested functional component React Native

I have this strange issue, keyboard keeps closing while typing when TextInput is placed inside Child Functional Component. This issue does not exist if TextInput is placed directly under Parent Component. Here is my code
const SignInScreenC = props => {
// define Hook states here
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isEmailEmpty,setIsEmailEmpty] = useState(false);
const [isEmailValid,setIsEmailValid] = useState(true);
const [isPasswordEmpty,setIsPasswordEmpty] = useState(false);
/**
* Called when Sign in is clicked.
* checks if the form is valid
*/
const _OnSignInClicked = () => {
if(_isFormValid()) {
//make api call
}
}
/* Checks if the form is valid
*/
const _isFormValid = () => {
//reset values
setIsEmailEmpty(false);
setIsEmailValid(true);
setIsPasswordEmpty(false);
let isValid = true;
if(email.trim() === "") {
setIsEmailEmpty(true);
isValid = false;
}
else if(!AppUtils.isEmailValid(email)) {
setIsEmailValid(false);
isValid = false;
}
else if(password.trim() === "") {
setIsPasswordEmpty(true);
isValid = false;
}
return isValid;
}
const SignInForm = () => {
return (
<View style={styles.formStyle}>
<TextInput
key="email"
label={Strings.hint_email}
value={email}
keyboardType="email-address"
onChangeText={(text)=> {
setEmail(text)
setIsEmailEmpty(false)
setIsEmailValid(true)
}}
style={styles.marginStyle}
autoCompleteType = "off"
scrollEnabled = {false}
autoCorrect={false}
autoCapitalize={false}/>
<TextInput
key="pass"
value={password}
secureTextEntry ={true}
label={Strings.hint_password}
style={[styles.marginStyle,styles.stylePassword]}
onChangeText={(text)=> {
setPassword(text)
setIsPasswordEmpty(false)}
}
theme="light"
autoCompleteType = "off"
scrollEnabled = {false}
autoCorrect={false}
autoCapitalize={false}/>
<Button
style={styles.loginStyle}
title = {Strings.login}
onPressButton = {() => _OnSignInClicked()}/>
</View>
);
}
return (
<>
<ImageBackground source={Images.screen_backgound} style={{width: '100%',
height: '100%'}}>
<View style = {styles.viewOverlaystyle} />
<ScrollView contentContainerStyle = {{flexGrow:1}}
keyboardShouldPersistTaps={'handled'}>
<View style={styles.containerStyle}>
<SignInForm/>
</View>
</ScrollView>
</ImageBackground>
</>
);
}
const styles = StyleSheet.create({
....
})
const mapStateToProps = state => ({
userData : state.userData
});
const mapDispatchToProps = dispatch =>
bindActionCreators(UserActions, dispatch);
const SignInScreen = connect(mapStateToProps,mapDispatchToProps) (SignInScreenC)
export {SignInScreen};
Everything works fine if I paste everything < SignInForm> directly to render method.
your SignInForm function (which is treated like React component, because its capitalized and called as JSX) is declared inside your SignInScreenC component. This means that every render, new type of React component is created.
SignInScreenC renders first time: creates SignInForm component, instantiates it and renders it
SignInScreenC renders second time: creates another, completely different SignInForm component, instantiates it again, effectively unmounting old SignInForm and rendering new SignInForm in it's place
since old input is unmounted, you lose keyboard focus
This is due to the way React handles rendering: whenever it encounters different type of element that should be rendered in place of an old element, old one will be unmounted. To react, every new SignInForm is different from the previous one as you keep constantly creating new functions
Solutions:
create separate SignInForm component outside of SignInScreenC and pass all the necessary data as props
or, instead of const SignInForm = () => return (...) use const renderSignInForm = () => return (...), and while rendering, instead of <SignInForm/> call it like {renderSignInForm()}. This way it will not be treated like a component and will not be a subject to unmounts
I had a slightly different but related issue trying to propage a text change to a parent component (React Native).
If your components bubbles up the onChangeText event and that triggers the re-render and ensuing lost of focus on keyboard, you can also consider propagating your change event onEndEditing instead once the user is done inputting text and keep a local state for the text entry.
export function YourTextInputComponent(
{ initialValue, onChangeTextDone } :
{ initialValue: string, onChangeTextDone : (text: string) => void) }
): JSX.Element {
const [text, setText] = useState<string>(initialValue);
return (
<TextInput
value={text}
onChangeText={(txt) => {
setText(txt);
}}
onEndEditing={(event) => {
onChangeTextDone(text);
}}
/>
)
}

Resources