Need formik values in handle submit outside the formik - react-native

I have this screen in react native where I want to validate the form and submit it and I have the submit button in my header. How can I get the values in handle submit which is outside the formik. Because I couldn't access the values in the custom function handleSubmit.
React.useLayoutEffect(() => {
navigation.setOptions({
headerTitleAlign: 'center',
headerTitle: () => <Text style={styles.titleHeader}>Payment Method</Text>,
headerRight: () => (
<Pressable onPress={handleSubmit}>
<Text>Done</Text>
</Pressable>
),
});
}, [navigation, userId]);
const handleSubmit = async (values: any) => {
const data: any = {
paymentServiceTypeId: defaultPaymentType,
invoiceNeeded,
companyName: company,
vatNumber,
}
console.log(values);
};
return (
<Formik
initialValues={initialState}
onSubmit={(values) => handleSubmit(values)}
validationSchema={getValidationSchema(staticValues.static)}
>
{({ handleChange, handleBlur, values, touched, errors, setFieldValue }) => (
<>
{defaultPaymentType == 2 && (
<>
<Text style={styles.title}>Company Name</Text>
<View style={styles.textInputView}>
<TextInput
style={styles.inputText}
placeholder={'Company Name'}
value={values.company}
onChangeText={handleChange('company')}
onBlur={handleBlur('company')}
/>
{touched.company && errors.company && <Text style=
{styles.errorText}>{errors.company}</Text>}
<>
)}
</Formik>

By using const formRef = useRef(); and then
<Formik
...
innerRef={formRef}
/>
Now where you need values, get them from formRef.current.values;

This issue can easily be fixed by using useFormik which separates the form from the values and handlers being used by formik. So, even if you have the submit button outside the form, you can use the handleSubmit provided by the Formik.

Related

React Native call function outside of App. Flatlist access onPress function

There is example https://reactnative.dev/docs/flatlist
Let's say I want to add button in each flatlist item. All happens inside App.js
const Item = ({ item,.....}) => (
<TouchableOpacity onPress={onPress} style={..}>
<Button title='Go' onPress={() => myFunc('abc')} /> </TouchableOpacity>);
const App = () => {
function myFunc(x){
}
}
I get " ReferenceError: Can't find variable: myFunc "
I solved this by moving Item inside of const App = () => { but I think it might be wrong.
Tell me please, what is the correct way?
You could do something like this:
const App = () => {
const myFunc = (args) => {
// perform your action here.
}
return (
<FlatList
data={[{ title: 'Title Text', key: 'item1' }]}
renderItem={({ item, index, separators }) => (
<TouchableOpacity
key={item.key}
onPress={() => myFunc('abc')}
>
<View style={{ backgroundColor: 'white' }}>
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
)}
/>
)
}
export default App;
Also you do not need to using TouchableOpacity if you are using Button Component already.
And since you are using separate component to render item for FlatList so it can be done as below:
// Considering App as Parent Component
const App = () => {
// Considering Item as separate Component
const Item = ({item, index, separators}) => {
return (
<TouchableOpacity
key={item.key}
onPress={() => myFunc('abc')}
>
<View style={{ backgroundColor: 'white' }}>
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
)
}
const myFunc = (args) => {
// perform your action here.
}
return (
<FlatList
data={[{ title: 'Title Text', key: 'item1' }]}
renderItem={Item}
/>
)
}
export default App;
All code are inside App Component;

how do I update useState immediately?

I'm trying to add and remove items from my movies favlist but I am unable to render things immediately with useState. I also trying to update favoritesFilm in UseEffect but my page crashed for continuing re-render.
This is my fav component:
export default function FavouriteBox() {
const navigation = useNavigation<NavigationProps>()
const [favoritesFilm, setFavorite] = useState<Movie[]>([])
const [isLoadingFav, setIsLoadingFav] = useState(true)
useEffect(() => {
getFav()
}, [])
useEffect(() => {
console.log(favoritesFilm)
}, [favoritesFilm])
async function removeMovie() {
const removed = StorageResources.storageRemove('favmovies')
setFavorite(favoritesFilm)
}
async function getFav() {
const favoriteMovies = await StorageResources.storageGet('favmovies')
setFavorite(favoriteMovies)
setIsLoadingFav(false)
}
const renderItemFav = ({ item }: any) => (
<FavMovie name={item.name} title={item.title} poster_path={item.poster_path} id={item.id} />
)
const FavMovie = ({ title, poster_path, name, id }: any) => (
<View style={styles.wrap}>
<Image
style={styles.image}
source={{
uri: `https://image.tmdb.org/t/p/w500/${poster_path}`,
}}
/>
{title && <Text style={styles.fav}>{title}</Text>}
{!title && <Text style={styles.fav}>{name}</Text>}
<MaterialCommunityIcons onPress={() => removeMovie()} name="bookmark-minus-outline" style={styles.book} />
</View>
)
return (
<View style={styles.container}>
<Text style={styles.title}>Preferiti</Text>
{isLoadingFav && <LoaderBox />}
{!isLoadingFav && (
<FlatList
data={favoritesFilm}
keyExtractor={(item) => item.id}
renderItem={renderItemFav}
horizontal
></FlatList>
)}
</View>
)
}
In my home component I use this function to add to fav:
const addToFavorites = async (item: Movie) => {
if (favorites.includes(item)) return null
StorageResources.storageSave('favmovies', [...favorites, item])
setFavorites([...favorites, item])
}
I would like to understand why it doesn't work and why every time I want to show movies in the favmovies component I have to refresh. (I used AsyncStorage for getItem and removeItem)

How do I make the first box as default selected?

I'd created custom radio boxes like as shown below.
But I want to make like as the first one should default selected like as shown below:
Here is the full code for this:
function Chips({ data, onSelect }) {
const [userOption, setUserOption] = useState(null);
const selectHandler = (value) => {
onSelect(value);
setUserOption(value);
};
return (
<View style={{flexDirection: "row"}}>
{data.map((item) => {
return (
<Pressable
style={[item.value === userOption ? styles.selected : styles.unselected, styles.commonChips]}
onPress={() => selectHandler(item.value)}>
<Text style={{color: item.value === userOption ? '#fff' : '#bfccd3', fontWeight: "bold"}}>{item.value}</Text>
</Pressable>
);
})}
</View>
);
}
export default function App(){
return (
<Chips data={data} onSelect={(value) => setOption(value)} />
)
}
You can set the default value while declaring state
const [userOption, setUserOption] = useState(data[0].value);

Trigger an onPress Function from anonther component

I want to trigger an onPress function from the search icon in the navbar.
This is the search component:
function SearchIcon(props) {
const theme = useSelector(state => state.themer.theme);
return (
<Icon.Button
name="search"
size={22}
color={theme.icons}
backgroundColor={theme.top_tab}
onPress={() => {}}
/>
);
}
export default SearchIcon;
The search component is being called in the specific stack, where it's needed.
<Stack.Screen
name="Home"
component={Home}
options={({navigation, route}) => ({
...,
headerRight: props => (
<View style={{flexDirection: 'row'}}>
<SearchIcon />
<CartIcon navigation={navigation} />
</View>
),
})}
/>
On the home screen, I have an isSeacrhing constant that should change value from false to true and vice versa.
const [data, setData] = React.useState({
isSearching: false,
search: '',
...
});
// TRIGGERED BY SEARCH ICON IN NAV BAR
const toggleSearch = () => setData({...data, isSearching: !isSearching});
{data.isSearching == false ? (
<ScrollView
...
</ScrollView>
) : (
<View>
<TextInput
style={[styles.textInput, [{color: theme.text, ...FONTS.body4}]]}
value={data.search}
placeholder="Search..."
placeholderTextColor={theme.text}
onChangeText={()=>{}}
/>
</View>
)}
Is it possible to trigger the onPress function or is there another way I can make it work? The search icon is on two screen, does calling the same function make the TextInput appear on both?
Wherever you use <SearchIcon /> just add a prop in that like this
<SearchIcon onPress={() => { // Do Something }} />
Then in your SearchIcon
function SearchIcon(props) {
const theme = useSelector(state => state.themer.theme);
return (
<Icon.Button
name="search"
size={22}
color={theme.icons}
backgroundColor={theme.top_tab}
onPress={props.onPress} // Access it here like this
/>
);
}
export default SearchIcon;

you need to specify name or key when calling navigate with an object as the argument

i'm having an messages screen and i need to navigate to a "single message" when tapping to the List item of messages but i get this error "you need to specify name or key when calling navigate with an object as the argument"
i have created the "single message" screen and added it as a <Stack.Screen/> also but i don't know what i'm doing wrong.
below is my code:
function MessagesScreen({navigation}) {
const [messages, setMessages] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const loadMessages = async () => {
const response = await messagesApi.getMessages();
setMessages(response.data);
}
useEffect(() => {
loadMessages();
}, []);
const handleDelete = message => {
setMessages(messages.filter((m) => m.id !== message.id));
}
return (
<Screen>
<FlatList
data={messages}
keyExtractor={message => message.id.toString()}
renderItem={({ item }) =>
<ListItem
title={item.fromUserId}
subTitle={item.content}
image={item.image}
onPress={() => navigation.navigate(routes.MESSAGE_SINGLE, item)}
renderRightActions={() =>
<ListItemDeleteAction onPress={() => handleDelete(item)} />}
/>
}
ItemSeparatorComponent={ListItemSeparator}
refreshing={refreshing}
onRefresh={() => {
setMessages([
{
id: 1,
title: 'T1',
description: 'D1',
image: require('../assets/mosh.jpg')
},
])
//setMessages(loadMessages());
}}
/>
</Screen>
);
}
const styles = StyleSheet.create({
})
export default MessagesScreen;
when i'm logging the "onPress" event on the console like this:
onPress={() => console.log('message selected', item)}
heres what i get:
and below is the MessageSingle screen i created to render the message but i dont know how to do it.
function MessageSingle() {
return (
<Screen>
<View style={styles.container}>
<AppText>{"kjhkjhjk"}</AppText>
{/* <AppText>{getMessagesApi}</AppText> */}
</View>
</Screen>
);
}
const styles = StyleSheet.create({
container: {}
});
export default MessageSingle;
so i want to get the message from the list of the messages. maybe i dont have to create e separate screen? i'm a beginner on this
any help would be appreciated!
you need to first add your MessageSingle component to the navigation container. Just put it as one of the screens along your MessagesScreencomponent. Then you need to navigate to it using that name:
onPress={() => navigation.navigate('MessageSingle', {item})}
the above will navigate to the screen with name MessageSingle, and passing the object item as a param.
in order to access this in your MessageSingle component, you need to use the route props.
function MessageSingle({route}) {
console.log('item = ', route.params?.item); // this would be your item.
return (
<Screen>
<View style={styles.container}>
<AppText>{"kjhkjhjk"}</AppText>
{/* <AppText>{getMessagesApi}</AppText> */}
</View>
</Screen>
);
}