react-hook-form: How to get the date in format hh:mm:ss of UTC timezone from react-datetime in localtime zone - react-hook-form

I have the following code
import "./styles.css";
import {
Col,
Row,
Form,
InputGroup,
Container
} from "reactstrap";
import React from "react";
import { useForm, Controller } from "react-hook-form";
import "react-datetime/css/react-datetime.css";
import Datetime from "react-datetime";
export default function App() {
const onSubmit = (data) => {
console.log(data);
};
const { control, handleSubmit, watch } = useForm({
defaualtValues: {
timevar: new Date(new Date().setHours(0, 0, 0, 0))
}
});
console.log(watch("timevar"));
return (
<Container>
<Form onSubmit={handleSubmit(onSubmit)}>
<Row className="m-3">
<Col>
<InputGroup className="mb-3">
<Controller
name="timevar"
control={control}
render={({ field }) => (
<Datetime
{...field}
timeFormat={"h:mm A Z"}
dateFormat={false}
value={field.value}
/>
)}
/>
</InputGroup>
</Col>
</Row>
</Form>
</Container>
);
}
The timevar will be in localtime zone
I want to get the value of timevar as hh:mm:ss and this will be a UTC time zone converted value
How can i do this. Is there any way i can do it in the component itself or do i have to get this done in the onSubmit function
Showing what I want in screenshot
codesandbox

I suggest you to add more information about how you are going to use that timevar, because it may dramatically ease your task.
If timevar should be processed anyhow after the form is submitted, for example sent via network request, then the easiest way is to convert it to UTC and then to string by doing:
const onSubmit = (data) => {
const { timevar } = data; // make sure that timevar is a moment object.
const result = timevar.utc().format("hh:mm:ss");
}
If you want to use string representation in render, then you could use useMemo to calculate the string on each date change, like:
import { useMemo } from "react";
...
const timevar = watch("timevar");
const timeString = useMemo(() => {
// Again, make sure that timevar is a moment object.
return timevar.utc().format("hh:mm:ss")
}, [timevar]);
// use timeString in render method.
And finally, if you don't care much about performance, and you feel pretty confident with moment's UTC functions you may transform all inputs and outputs on the input component, like this:
<Controller
...
render={({ field }) => (
<DateTime
...
value={moment.utc(field.value, "hh:mm:ss")}
onChange=(e => {
// Make sure that DateTime pass a moment object to onChange.
field.onChange(
e.target.value.utc().format("hh:mm:ss")
);
}
}
/>

Related

How to setValue and getValue when using useFormContext in map Function React hook form

How to setValues in textField and get Values when updates it.
Child Component
const MyTextField = () => {
const { control } = useFormContext()
<Controller control={control}
name={name}
render={({
field: {onChange, onBlur, value},
fieldState: { error }
}) => {
return (<TextField fullWidth />
)
}}
/>
)
}
export default MyTextField
let data = [ 100, 200, 300]
data.map(item => (<MyTextField name='price' value={item} />)
If you want to somehow transform the input value whenever it changes you can do this
const MyTextField = () => {
const { control } = useFormContext()
<Controller control={control}
name={name}
render={({
field,
fieldState: { error }
}) => {
return (<TextField {...field} fullWidth onChange={({target:{value}}) => {
// do somethign with the value
// and then call the field onChange to update the field state
field.onChange(value)
}} />
)
}}
/>
)
}
export default MyTextField
Yo don't need to call getValues while you're inside Controller, that's field.value for. If you wnat to get the input value outside the Controller then the getValue must be the one returned by useForm

react-hook-form custom resolver only checking after submit

I'm building an abstract form component with react-hook-form and Yup for validation. The form works, and validation works, but only after the submit button is pressed.
It's on codesandbox, but ...
import React, { cloneElement } from "react";
import "./styles.css";
import { Controller, FormProvider, useForm } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import { string as yupString, object as yupObject } from "yup";
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
TextField
} from "#mui/material";
let renderCount = 0;
export const FormContent = ({ content }) => {
return content.map((item, i) => {
const name = item.component.props.name;
return (
<Controller
key={name + "_" + i}
name={name}
defaultValue=""
render={({ field, fieldState: { error }, formState: { isDirty } }) => {
return cloneElement(item.component, {
...field,
error: isDirty && !!error,
helperText: isDirty && error?.message,
FormHelperTextProps: { error: true }
});
}}
/>
);
});
};
export default function App() {
renderCount++;
const usernameInput = {
validation: yupString().required("Username is required"),
component: (
<TextField required label="Username" name="username" type="text" />
)
};
const passwordInput = {
validation: yupString().required("Password is required"),
component: <TextField required label="Password" name="password" />
};
const content = [usernameInput, passwordInput];
let validationSchema = yupObject().shape({});
// construct schema
content.forEach((item) => {
validationSchema = validationSchema.concat(
yupObject().shape({
[item.component.props.name]: item.validation
})
);
});
const methods = useForm({
resolver: yupResolver(validationSchema)
});
const onFormSubmit = (data) => {
console.log(data);
};
return (
<Dialog open>
<Box>Render Count: {renderCount}</Box>
<FormProvider {...methods}>
<Box component="form" onSubmit={methods.handleSubmit(onFormSubmit)}>
<DialogContent>
<FormContent content={content} />
</DialogContent>
<DialogActions>
<Button
type="submit"
fullWidth
name="login"
variant="contained"
color="primary"
size="large"
>
Login
</Button>
</DialogActions>
</Box>
</FormProvider>
</Dialog>
);
}
If you type some data in the fields, and then erase the data without pressing the button, nothing happens. If you leave the fields empty and press the button, it gives the native component error message for required (i.e., it doesn't do the Yup resolving). But, if you enter some data, press the button, and then erase the data, then the Yup validation kicks in. How do I make it work before the button is pressed?
You need to remove required prop from input components because otherwise native html validation will kick in.
And if you want start validation before pressing submit button you need to use some other mode for form, for example:
const methods = useForm({
resolver: yupResolver(validationSchema),
mode: 'onChange' // or 'onBlur' for example
});
Codesandbox
More info in the docs

How to keep popup of Quasar Select component open?

I'm working to create a geocoding component that allows a user to search for their address, using Quasar's <q-select /> component. I'm running in to one issue with the popup however.
After a user enter's the search query, I fetch the results from an API and the results are set to a reactive local state (which populates the select's options). Instead of the popup displaying though, it closes, and I have to click on the chevron icon twice for the popup to display the results.
This first image is what it looks like when I first click in to the input.
The second image shows what happens after entering a query. The data is fetched, options are set, and the popup closes.
The third image shows the select after clicking on the chevron icon twice.
How do I programmatically show the popup, so that once the results are fetched, the popup is displayed correctly?
Edit: Created a working repro here.
<template>
<q-select
ref="geolocateRef"
v-model="state.location"
:options="state.locations"
:loading="state.loadingResults"
clear-icon="clear"
dropdown-icon="expand_more"
clearable
outlined
:use-input="!state.location"
dense
label="Location (optional)"
#clear="state.locations = undefined"
#input-value="fetchOptions">
<template #prepend>
<q-icon name="place " />
</template>
<template #no-option>
<q-item>
<q-item-section class="text-grey">
No results
</q-item-section>
</q-item>
</template>
</q-select>
</template>
<script lang='ts' setup>
import { reactive } from 'vue';
import { debounce, QSelect } from 'quasar';
import { fetchGeocodeResults } from '#/services';
const state = reactive({
location: undefined as string | undefined,
locations: undefined,
loadingResults: false,
geolocateRef: null as QSelect | null,
});
const fetchOptions = debounce(async (value: string) => {
if (value) {
state.loadingResults = true;
const results = await fetchGeocodeResults(value);
state.locations = results.items.map(item => ({
label: item.title,
value: JSON.stringify(item.position),
}));
state.loadingResults = false;
state.geolocateRef?.showPopup(); // doesn't work?
}
}, 500);
</script>
I'd also posted this question over in the Quasar Github discussions, and someone posted a brilliant solution.
<template>
<q-select
v-model="state.location"
:use-input="!state.location"
input-debounce="500"
label="Location (optional)"
:options="options"
dense
clear-icon="bi-x"
dropdown-icon="bi-chevron-down"
clearable
outlined
#filter="fetchOptions">
<template #prepend>
<q-icon name="bi-geo-alt" />
</template>
<template #no-option>
<q-item>
<q-item-section class="text-grey">
No results
</q-item-section>
</q-item>
</template>
</q-select>
</template>
<script lang='ts' setup>
import { reactive, ref } from 'vue';
import { QSelect } from 'quasar';
import { fetchGeocodeResults } from '#/services';
interface Result {
position: {
lat: number;
lng: number;
}
title: string;
}
const state = reactive({
...other unrelated state,
location: undefined as string | undefined,
});
const options = ref([]);
const fetchOptions = async (val: string, update) => {
if (val === '') {
update();
return;
}
const needle = val.toLowerCase();
const results = await fetchGeocodeResults(needle);
options.value = results.items.map((item: Result) => ({
label: item.title,
value: JSON.stringify(item.position),
}));
update();
};
</script>

How to save json data displayed on simulator to local storage (React-native)

I am using react-native and expo. I have two screen. When user clicks on first screen symbol such as
First screen:
A
acompany
B
bcompany
User clicks on A symbol. I fetch some json data for that symbol and display on my second screen, but I want that fetched data to be saved on the second screen. But right now if I go back to first screen(it has some json data such as symbol name A and what it stands for acompany etc as given above) and come back to second screen the data get disappeared. Is there any way to store my data of second screen to the local storage? I have comment the line in second class that I would like to store in local storage
My second class:
import React, { useState, useEffect } from "react";
import {
TouchableWithoutFeedback,
Keyboard,
FlatList,
TextInput,
Button,
Text,
} from "react-native";
import {
StyleSheet,
View
} from "react-native";
import { useStocksContext } from "../contexts/StocksContext";
import { scaleSize } from "../constants/Layout";
export default function StocksScreen({ route }) {
const { ServerURL, watchList } = useStocksContext();
const [state, setState] = useState({
myListData: [],
});
const { stuff } = route.params;
renderWithData = () => {
return fetch(`http://131.181.190.87:3001/history?symbol=${stuff}`)
.then((res) => res.json())
.then((json) => {
setState({
isLoaded: true,
myListData: json,
});
});
};
useEffect(() => {
renderWithData();
}, [watchList]);
let item = state.myListData.length && state.myListData[0];
//that's the data I would like to save on local storage (movie)
let movie = (
<View style={styles.text}>
<Text style={styles.text} key={item.symbol}>
{item.high}
</Text>
<Text style={styles.text} key={item.symbol}>
{item.close}
</Text>
</View>
)
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<Text style={styles.text}>bb</Text>
<View>{movie}</View>
</View>
</TouchableWithoutFeedback>
);
}
const styles = StyleSheet.create({
text: {
color: "black",
backgroundColor: "white",
},
});
Third class: Here I would like to save my data to local storage
import React, { useState, useContext, useEffect } from "react";
import { AsyncStorage } from "react-native";
const StocksContext = React.createContext();
export const StocksProvider = ({})
const [state, setState] = useState([]);
return (
<StocksContext.Provider value={[state, setState]}>
</StocksContext.Provider>
);
}}
How can I store my second screen data to the local storage? Is there any way to do that? I have never done this before such as storing data locally. I even went through a couple tutorials and looked online, but couldn't figure out. Any help is appreciated.
To store data locally, you can use React Native AsyncStorage to fetch saved data: getItem(). Also you can use store function by Redux. When creating your Redux store, pass your createStore function a persistReducer that wraps your app's root reducer. Once your store is created, pass it to the persistStore function, which ensures your redux state is saved to persisted storage whenever it changes.store,
To store data locally, you have to use an abstraction on top of AsyncStorage instead of AsyncStorage directly for anything more than light usage since it operates globally.
import React, { useState, useContext, useEffect } from "react";
import { AsyncStorage } from "react-native";
const StocksContext = React.createContext();
export const StocksProvider = ({})
const [state, setState] = useState([]);
storeData = async () => {
try {
await AsyncStorage.setItem(
'Your Key',
state
);
} catch (error) {
// Error saving data
}
};
retrieveData = async () => {
try {
const value = await AsyncStorage.getItem('Your Key');
if (value !== null) {
// We have data!!
console.log(value);
}
} catch (error) {
// Error retrieving data
}
};
return (
<StocksContext.Provider value={[state, setState]}>
</StocksContext.Provider>
);
}}
for the detail follow this link :
https://reactnative.dev/docs/asyncstorage

Should a component adapter wrap just the component or should it also wrap <Field>

Best concept for wrapping react-final-form components
Background
I'm using react-native and will probably start using react-final-form as soon as I have wrapped my head around it. I will build a component library based on react-native-paper.
All examples I've seen on wrapping 3:rd party components for use as react-final-form fields (i.e. <Field>), does not wrap the actual field component. Instead the wrapped component is injected via the component property of the Field component.
If I'm doing all the work wrapping the components, why not go all the way? Since I haven't found any examples of the "complete wrapping", I'm kind of worried that it's not a good idea.
Both solutions seems to work fine, btw. The code below is psuedo:ish.
Only wrapping the component
export default function App() {
const CheckboxAdapter = ({input: {onChange, value}, meta}) => (
<Checkbox
status={value}
onPress={() => {
onChange(value === 'checked' ? 'unchecked' : 'checked');
}}
errorText={meta.touched ? meta.error : ''}
/>
);
return (
<Form
..
render={({handleSubmit}) => {
return (
..
<Field
name="myFieldname"
component={CheckboxAdapter}
/>
)
}
/>
)
}
Wrapping the component inside of the <Field> component
export default function App() {
const MyCheckbox = ({name}) => (
<Field
name={name}
component={({input: {onChange, value}, meta, children, ...rest}) => (
<Checkbox
status={value}
onPress={() => {
onChange(value === 'checked' ? 'unchecked' : 'checked');
}}
errorText={meta.touched ? meta.error : ''}
/>
)};
/>
);
return (
<Form
..
render={({handleSubmit}) => {
return (
..
<MyCheckbox
name="myFieldname"
/>
)
}
/>
)
}
Not much interest in this question. I ended up wrapping the <Field>as well. This gives much more readable form code.
import React from 'react';
import TextInput from '../components/TextInput';
import {Field} from 'react-final-form';
const TextInputAdapter = ({input, meta, ...rest}) => {
const onChangeText = value => {
input.onChange(value);
};
return (
<TextInput
{...input}
{...rest}
onChangeText={onChangeText}
errorText={meta.touched ? meta.error : ''}
/>
);
};
const TextInputField = ({...rest}) => {
return <Field component={TextInputAdapter} {...rest} />;
};
export default TextInputField;

Resources