How to acces useRef value Hook? - reactjs

I am trying to implement a react native image crop view and I am using :
https://github.com/hhunaid/react-native-image-crop-tools#readme
After a bit of fixing it works and apparenty there is automatically an output generated:
https://ibb.co/SBLvFGY
My question is, how can I get the value of the useRef Hook, since I want to store it in redux, it all is immensly confusing to me, the code :
import React, { useState, useRef } from 'react';
import { Button, StatusBar, StyleSheet, View, Image } from 'react-native';
import { CropView } from 'react-native-image-crop-tools';
import ImagePicker from 'react-native-image-picker';
import { connect } from 'react-redux';
import { changeNewIdeaImage } from '../../redux/actions/';
const TestImageCropper = () => {
const [uri, setUri] = useState();
const cropViewRef = useRef();
return (
<>
<StatusBar barStyle="dark-content" />
<View style={styles.container}>
<Button
title={'Pick Image'}
onPress={() => {
ImagePicker.launchImageLibrary(
{ noData: true },
(response) => {
setUri(response.uri);
}
);
}}
/>
{uri !== undefined && (
<CropView
sourceUrl={uri}
style={styles.cropView}
ref={cropViewRef}
onImageCrop={(res) => console.warn(res)}
keepAspectRatio
aspectRatio={{ width: 4, height: 4 }}
/>
)}
<Button
title={'Get Cropped View'}
onPress={() => {
cropViewRef.current.saveImage(true, 90);
}}
/>
</View>
</>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
cropView: {
flex: 1,
backgroundColor: 'red',
},
});
const mapStatetoProps = (state: any) => {};
export default TestImageCropper;
Now this is the most confusing part to me.Where does the saveImage view function even come from and how can I hook it up, so that I can save the Image uri in Redux, to display it?
<Button
title={'Get Cropped View'}
onPress={() => {
cropViewRef.current.saveImage(true, 90);
}}
/>
Thanks in advance!

The saveImage() seems like a method coming from CropView. Ref is simply storing - in your case - the rendered CropView element, so cropViewRef.current is basically the CropView in your TestImageCropper component, and you call it's built-in method with cropViewRef.current.saveImage(true, 90);
What not clear to me, is what is it that you want to store in redux? The value of the ref is the element itself.

Related

Radio button value not resetting

I have a list of questions. And on click of next or previous I'm changing the question. I have 4 or 5 answers as radio buttons. I' using this package Radio button npm. And now I'm trying to change the question using states. I need the radio button unselected on changing of question. All content changing easily expect radio button resetting. Here is my code:-
<ScrollView>
<RenderHtml
contentWidth={width}
source={{html:question}}
/>
<OptionsView options={options} selectedBtn={(e) =>updateAnswer(e.option)}/>
</ScrollView>
Functional component
const OptionsView=(props)=>{
return(
<RadioButtonRN
style={{marginHorizontal:5,marginTop:1,margin:5}}
deactiveColor={'gray'}
activeColor={'green'}
initial={0}
boxStyle={{height:50}}
data={props.options}
icon={
<Icon
name="check-circle"
size={25}
color="green"
/>
}/>
)}
I solved it using below code:-
import React, { useState } from "react";
import { View, StyleSheet, Button, Alert } from "react-native";
import RadioButtonRN from 'radio-buttons-react-native';
const App = () => {
const [show,setShow] = React.useState(true);
const data = [
{
label: 'data 1'
},
{
label: 'data 2'
}
];
React.useEffect(()=>{
if(!show) setShow(true)
},[show])
const resetHandler = () =>{
setShow(false)
}
return (
<View style={styles.container}>
{show &&
<RadioButtonRN
data={data}
selectedBtn={(e) => console.log(e)}
/>
}
<Button title='reset' onPress={resetHandler} />
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingTop:100,
}
});
export default App;
Reference:- Radio Reset Button
If you are rendering your options base on the props that has been passed from parent component , U need to also include this hook in order to update your child component:
const OptionsView = (props.options) => {
const [options, setOptions] = useState(props.options);
useEffect(() => {
setOptions(props.options)
}, [props.options]
return (
<RadioButtonRN/>
)
}

Undefined error while trying to map over array of objects React Native

I've been trying to map an array of items which is passed from my Home Component to another but I'm always getting the error restaurants.map is Undefined. I fetch the data from Yelp and store it in a State(Home Component), then I pass this to another component, retrieve it through props and try to map through it(RestaurantItems Component). Please help. I have attached the code of my two components..
Home Component
import { View, Text, StatusBar, ScrollView } from "react-native";
import React, { useEffect, useState } from "react";
import HeaderTabs from "../components/HeaderTabs";
import SearchBar from "../components/SearchBar";
import Categories from "../components/Categories";
import RestaurantItems from "../components/RestaurantItems";
const YELP_API_KEY =
"6NE-noDkyFUDKVVo2B8POXtwsAIBEe7QTnZEwMpvNE-5asFQ1q0_jh7iJ5KqprLD3sVt2htFrZbe4V2rHbdXgUcqkMSquQADGcGOTh_ANZ1DRT_tnFKZBHT4Hh0eYn";
export default function Home() {
const [restaurantData, setRestaurantData] = useState();
const getRestaurantFromYelp = async () => {
const response = await fetch(
`https://api.yelp.com/v3/businesses/search?term=restaurants&location=san+francisco`,
{
method: "GET",
headers: {
Authorization: `Bearer ${YELP_API_KEY}`,
},
}
);
const data = await response.json();
setRestaurantData(data);
};
useEffect(() => {
getRestaurantFromYelp();
}, []);
return (
<View style={{ backgroundColor: "#eee", flex: 1 }}>
<StatusBar barStyle="dark-content" backgroundColor="#eee" />
<View style={{ backgroundColor: "#fff", padding: 15 }}>
<HeaderTabs />
<SearchBar />
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<Categories />
<RestaurantItems restaurants={restaurantData} />
</ScrollView>
</View>
);
}
RestaurantItems Component
import React from "react";
import { MaterialCommunityIcons } from "react-native-vector-icons";
export default function RestaurantItems({ restaurants }) {
console.log(restaurants);
return (
<View>
{restaurants.map((single_data) => (
<Text>{single_data.name}</Text>
))}
</View>
);
}
This has been asked so many times!
Your state is not initialized, so at first render, when your datas are not loaded yet, you have an error. So initiliaze your state with an empty array
const [restaurantData, setRestaurantData] = useState([]);

How to see the full details of particular member when it is clicked after search?

I have build a search engine and I am able to fetch data successfully the only thing I want to do is whenever that searched field is selected, it will show the details. (There are further details are also present like age,house_no etc.) It is like another screen have to come with full details.
I am stuck here, please help me out. It is like navigation to another screen with full details for now I am using alert command to show the some details I want another screen please. Thanks.
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
Text,
StyleSheet,
View,
FlatList,
TextInput,
} from 'react-native';
import { db } from '../firebase';
const App = () => {
const [search, setSearch] = useState('');
const [filteredDataSource, setFilteredDataSource] = useState([]);
const [masterDataSource, setMasterDataSource] = useState([]);
useEffect(() => {
const fetchData = async () => {
const data = await db
.collection('Data')
.get();
setFilteredDataSource(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
setMasterDataSource(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
fetchData();
}, []);
const searchFilterFunction = (text) => {
if (text) {
const newData = masterDataSource.filter(
function (item) {
const itemData = item.FM_NAME_EN + item.EPIC_NO + item.MOBILE_NO
? item.FM_NAME_EN.toUpperCase() + item.EPIC_NO.toUpperCase() + item.MOBILE_NO
: ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilteredDataSource(newData);
setSearch(text);
} else {
setFilteredDataSource(masterDataSource);
setSearch(text);
}
};
const ItemView = ({item}) => {
return (
<Text
style={styles.itemStyle}
onPress={() => getItem(item)}>
{item.FM_NAME_EN}
{'\n'}
{item.GENDER.toUpperCase()}
</Text>
);
};
const ItemSeparatorView = () => {
return (
// Flat List Item Separator
<View
style={{
height: 0.5,
width: '100%',
backgroundColor: '#C8C8C8',
}}
/>
);
};
const getItem = (item) => {
alert('Name : ' + item.FM_NAME_EN + ' Epic_no : ' + item.EPIC_NO);
};
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<TextInput
style={styles.textInputStyle}
onChangeText={(text) => searchFilterFunction(text)}
value={search}
underlineColorAndroid="transparent"
placeholder="Search Here"
/>
<FlatList
data={filteredDataSource}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={ItemSeparatorView}
renderItem={ItemView}
/>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
},
itemStyle: {
padding: 10,
},
textInputStyle: {
height: 40,
borderWidth: 1,
paddingLeft: 20,
margin: 5,
borderColor: '#009688',
backgroundColor: '#FFFFFF',
},
});
export default App;
first of all, you have to create ItemDetails.js as the new component for the new screen:
import React from 'react';
import {Text, View} from 'react-native';
const ItemDetails= () => {
return (
<View>
<Text>ItemDetails screen</Text>
</View>
);
};
export default ItemDetails;
then, make sure to install react native navigation and go to create a new screen:
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name="ItemDetails" component={ItemDetails} />
</Stack.Navigator>
then get back to the app component and modify your getItem function to this:
const getItem = item => {
navigation.navigate('ItemDetails', {item});
};
this is how you tell the application that it has to move to another screen, and you sent the specific item with it, now let's modify our ItemDetails.js to see the data:
const ItemDetails = ({route}) => {
//console.log(route.params) so you can know how to access whatever you want to print
return (
<View>
<Text>{route.params.item.item.FM_NAME_EN}</Text>
</View>
);
};
If I understood your question correctly, you need another route:
First, create a component for the new page that will display the details:
ItemDetails.js:
import { useParams } from 'react-router-dom' //You will get the params from the url
import {useState, useEffect} from 'react'
export const ItemDetails = props => {
const { id } = useParams()
const [item, setItem] = useState(null)
useEffect(() => {
if(id){
const itemSelected = /* get your item by id */
setItem(itemSelected)
}
}, [id])
return(
<p> {item.name} </p>
)
}
Wherever you define your routes:
import {ItemDetails} from './path/to/ItemDetails/'
<Switch>
...
<Route exact path="/item/:id" component={ItemDetails} />
...
<Switch>
Lastly, wrap your jsx with react-router link:
import { Link } from 'react-router-dom'
...
return (
<Link to={`/item/${item.id}`}>
<Text
style={styles.itemStyle}
{item.FM_NAME_EN}
{'\n'}
{item.GENDER.toUpperCase()}
</Text>
</Link>
);

React native - Invariant Violation: Changing onViewableItemsChanged on the fly is not supported

I was trying to build an image carousel in react native and came across the following error :
Invariant Violation: Changing onViewableItemsChanged on the fly is not supported
This error only appears on alternate reloads.
I've tried using useRef hook instead of useCallback and it solves the problem, but I don't understand why useCallback can't fix this
Code :
import React, { useState, useCallback } from 'react'
import { View, FlatList, Image, useWindowDimensions } from 'react-native'
import styles from './styles'
interface imagesProperties {
images: string[];
}
const ImageCarousal = ({ images } : imagesProperties) => {
const [activeIndex, setActiveIndex] = useState(0);
const windowWidth = useWindowDimensions().width;
const onCarousalChange = useCallback( (viewableItems) => {
if(viewableItems.changed.length>0)
setActiveIndex(viewableItems.changed[0].index);
}, [])
return (
<View style={styles.root}>
<FlatList
data={images}
renderItem={ ({ item }) => <Image style={[styles.image, { width: windowWidth-40 }]} source={{ uri : item }} /> }
horizontal
showsHorizontalScrollIndicator={false}
snapToInterval={ windowWidth-20 }
snapToAlignment={'center'}
decelerationRate={'fast'}
viewabilityConfig={{ viewAreaCoveragePercentThreshold: 50 }}
onViewableItemsChanged={onCarousalChange}
/>
<View style={styles.dotContainer}>
{ images.map( (image, index) => <View key={index} style={[styles.dot, { backgroundColor: (activeIndex == index) ? '#d1d1d1':'#fff' }]}></View> ) }
</View>
</View>
)
}
export default ImageCarousal

EXPO: Setting up react-native-action-sheet

Currently trying to setup react-native-action-sheet and getting an invalid hook call
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I followed the example and wrapped my wrapped your top-level component with even when using hooks.
export default () => (
<ActionSheetProvider>>
<App />
</<ActionSheetProvider>>
);
Wondering if it's the way I set this up:
import { Linking } from 'react-native';
import { useActionSheet } from '#expo/react-native-action-sheet';
export const SideButton = () => {
const { showActionSheetWithOptions } = useActionSheet();
const cancelButtonIndex = 1;
const options = ['Email', 'Cancel'];
const title = 'Email Me';
const message = 'Let me know your issues';
return showActionSheetWithOptions(
{
options,
cancelButtonIndex,
title,
message,
},
(buttonIndex) => {
if (buttonIndex === 0) {
Linking.openURL('mailto:_____').catch();
} else {
return;
}
}
);
};
Or even how I call it here:
import { Linking, Text, TouchableOpacity, View } from 'react-native';
import { SideButton } from './utils/HelpPopUp';
const ButtonContainer = () => (
<TouchableOpacity>
<Text onPress={() => Linking.openURL('_MY_WEBSITE_').catch()}>Checkout my stuff</Text>
</TouchableOpacity>
);
const Menu = (props) => {
return (
<View>
<ButtonContainer />
</View>
);
};
export default Menu;
Sorry, my answer would be to suggest an alternative rather than answer your question and present the component I use myself. I did not use the package you mentioned in the topic, but I tried with a different package before, it does the same job. https://www.npmjs.com/package/react-native-actions-sheet
This is the custom component I used.
import React from 'react';
import {StyleSheet, Text, View} from 'react-native';
import ActionSheet from 'react-native-actions-sheet';;
const CustomActionSheet = React.forwardRef(({children, title}, ref) => {
return (
<ActionSheet
ref={ref}
headerAlwaysVisible={true}
containerStyle={[
styles.containerStyle,
{backgroundColor: '#FFF'},
]}
CustomHeaderComponent={
<View style={[styles.header, {backgroundColor: '#4ac'}]}>
<Text style={styles.title}>
{title}
</Text>
</View>
}>
{children}
</ActionSheet>
);
});
const styles = StyleSheet.create({
header: {
height: 50,
justifyContent: 'center',
padding: 5,
},
title: {
color: '#FFF',
fontSize: globalStyles.titleText.fontSize,
},
containerStyle: {
borderRadius: 0,
},
});
export default CustomActionSheet;
Usage:
import React, { createRef } from "react";
import {View, Text} from "react-native";
import CustomActionSheet from './CustomActionSheet';
const actionSheetRef = createRef();
const AnythingPage = () => {
return (
<View>
<Text onPress={() => actionSheetRef.current?.show()}>
Open Custom Sheet
</Text>
<CustomActionSheet ref={actionSheetRef} title={'Title'}>
<View>
<Text>Add anything in it.</Text>
</View>
</CustomActionSheet>
</View>
)
}
You can develop a structure that will be used in the whole application by going through these.

Resources