Expo Barcode Scanner show output with line break - react-native

i have created a barcode/QR scanner react native app using expo, and everything works fine, i can scan Qrcodes and get results, However the issue arises when i want to display the output. The data object returned is displayed as a single line although it has a couple of properties within it. I want to add line breaks to it at the end of any property(ends with semi colon) so i decided to use regex to replace the semi colon at the end of any property with a new line flag however that doesn't work.{data.replace(/;/g, "\n")} How can i achieve this
useEffect(() => {
const getBarCodeScannerPermissions = async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === "granted");
};
getBarCodeScannerPermissions();
}, []);
const handleBarCodeScanned = ({ type, data }) => { //data parsed from the Qr code
setObjectValue (data,type)
setScanned(true);
setData(data)
setMainModal(true)
};
return (
<View style={styles.container}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={[StyleSheet.absoluteFillObject, styles.minicontainer]}
/>
<MainModal> //modal popup containing the scanned QRinformation to be shown
<Text style={{ whiteSpace: "pre-line" }}>
{data.replace(/;/g, "\n")}
</Text>
</MainModal>
</View>
)

Hmmm it seems like that should work. I guess you could try splitting and then mapping?
{data.split(";").filter(t=>t && t.trim().length>0).map(text=>(
<Text style={{whiteSpace:'pre-line'}}>
{text}{'\n'}
</Text>
}

Related

I'm trying to call a function defined on the web, but nothing happens

I'm trying to open a simple page with React Native WebView.
It's a single page web, and when you do a search, it prints out some information about your search.
After that, if you want to search again, press the back button on the device to move to the search box.
Because it is a single page, I cannot use goBack, so I created a function called cancel.
The problem is that when I click the device's back button, the function called cancel defined on the web is not executed.
The cancel function deletes the searched information and returns to the search window.
I will upload my code.
Please advise.
export default function App() {
const webviewRef = useRef(null);
const backAction = () => {
setBackTapping((prev) => prev += 1);
webviewRef.current.injectJavaScript('window.cancel()')
return true;
}
useEffect(() => {
const timer = setInterval(() => {
setBackTapping(0)
}, 1000)
return () => clearInterval(timer);
}, [])
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress',backAction);
return () => backHandler.remove()
}, [])
useEffect(() => {
if(backTapping >= 2){
return BackHandler.exitApp();
}
},[backTapping])
return (
<KeyboardAvoidingView style={{ flex: 1 }}>
<StatusBar hidden />
<WebView
ref={webviewRef}
textZoom={100}
originWhitelist={['*']}
javaScriptEnabled
source={{ uri: 'myhome.com'}}
startInLoadingState={true}
/>
</KeyboardAvoidingView>
);
}
Expected behavior:
The cancel function is executed, all open windows are closed, and you are returned to the search window.
in my case, calling is wrong.
instead of :
webviewRef.current.injectJavaScript('window.cancel()')
use :
const generateOnMessageFunction = (data) => `
(function(){
window.dispatchEvent(new MessageEvent('message',{data: ${JSON.stringify(data)}}));
})()
`;
webviewRef.current.injectJavaScript(generateOnMessageFunction('cancel'));
detail referance :
https://github.com/react-native-webview/react-native-webview/issues/809

Why my input closes when new Component is showing?

I have a a Header there is a text input and I have a Main component there are the list of products of the searched text.
So If the input text is empty then I want to show him his last searched things. If he type anything then I want to show him the products with the same name as the input text. I make a condition like this:
const SearchRoot = ({ }: ISearchRoot) => {
const searchParam = useSelector((state: RootState) => state.Search.searchParam);
return (
<>
{ searchParam.length > 0 ?
<SearchList />
:
<LatestHistory />
}
</>
)
}
So if I type anything then my input are closing automatically. But if I press again the keyboard and typing then its not closing and working. So its only happend when the component changing from <LatestHistory to <SearchList only one time. So how can I make my keyboard always open when the component is chaning ?
Search
const Search = () => {
return (
<View style={s.container}>
<StatusBar backgroundColor='#fff' />
<SearchHeader />
<SearchRoot />
</View>
)
}
SearchHeader
const SearchHeader = () => {
const navigation = useNavigation<NativeStackNavigationProp<RootStackParams>>();
const dispatch = useDispatch();
// filters
const [searchText, setSearchText] = useState<string>('');
const handleChangeText = (e: string) => {
dispatch(setSearch({searchParam: e}));
setSearchText(e)
};
const handleClearText = () => {
dispatch(setSearch({searchParam: ''}));
setSearchText('');
};
const handlePressSeach = () => {
searchText.length > 0 &&
navigation.navigate('Searched', {
searchText,
searchType: tabType
});
};
const handleGoBack = () => navigation.goBack();
return (
<View style={s.container}>
<View style={s.header}>
<View style={s.backContainer}>
<GoBackIcon
onPress={handleGoBack}
color='#555'
/>
</View>
<View style={s.inputContainer}>
<SearchInput
value={searchText}
onChangeText={handleChangeText}
onPressSearched={handlePressSeach}
onPressClearTextField={handleClearText}
autoFocus={true}
style={s.sInput}
/>
</View>
</View>
</View>
)
}
SearchRoot
const SearchRoot = ({ }: ISearchRoot) => {
const searchParam = useSelector((state: RootState) => state.Search.searchParam);
return (
<>
{ searchParam.length > 0 ?
<SearchMain />
:
<Text>Vorschläge</Text>
}
</>
)
}
I am very thankful for your help!!
I just read your problem statement and matched it with your Code.
✦First of all your sequence is wrong.
✦First Comes the Previous Searched Result.
✦Then when you press it then it should clear the previous search result.
✦Then On text change Property
✦Then OnPress Searched
Maybe that's Why it closes. If that's Not the case then!
(In my Opinion)
when then you click the search bar the previously searched result disappears and this is done by the function handleClearText. Now When the Function Completes it's Functionality the search bar closes. The Second time you open the search bar it works as their's no Previous Search result to clear.
So the Problem is with the handleClearText function.
My Proposed Solution in case 2 is
We can call the search method again (at the end) of handleClearText function.
then it will re open the search bar with the new state.
I hope it resolves your issue, Regards...
first of all const [searchText, setSearchText] = useState(''); this whole logic move to parent class search and from there pass it to siblings to avoid any confusion.whenever re-rendering happens values will be passed correctly to child.I think somewhere that is going wrong.Either use state and prop drilling or store, dont mix it up.

TextInput component suggests

enter image description here
https://i.stack.imgur.com/AEGy7.png
I have a TextInput component how can I write content and it suggests the next word that is highlighted as the image
when i enter F and it automatically adds b.com is blacked out
You can have a list with common keywords that you use in your app, something like this:
[
{
"id": 1,
"name": "chicken",
}
{
"id": 2,
"name": "banana",
}
]
Note that ideally you would be getting this data from an API and not manually add it yourself.
Then, make a simple search bar and declare a new variable using useState:
const [filter, setFilter] = useState([])
...
<TextInput style={styles.textinput} placeholder='Search food...' onChangeText={handleChange}/>
The handleChange function is in charge of setting the new data Filter:
const handleChange = (e) => {
const currentFilter = data.filter((item) => {
return item.name.toLowerCase().includes(e.toLowerCase())
})
setFilter(currentFilter)
}
For displaying the data that you want, you can use conditional rendering. Just insert the following snippet inside the View in which you want to display your data:
{
filter.length != 0 && (
<View>
{
filter.map((item, key) => {
return <Text key={item.id} style={styles.textstyle}>{item.name}</Text>
})
}
</View>
)
}
Here is the full code:
export default function FilteringTest() {
const [filter, setFilter] = useState([])
const handleChange = (e) => {
const currentFilter = data.filter((item) => {
return item.name.toLowerCase().includes(e.toLowerCase())
})
setFilter(currentFilter)
}
return (
<View style={styles.background}>
<TextInput style={styles.textinput} placeholder='Search food...' onChangeText={handleChange} />
{
filter.length != 0 && (
<View>
{
filter.map((item, key) => {
return <Text key={item.id} style={styles.textstyle}>{item.name}</Text>
})
}
</View>
)
}
</View>
)
}
As for highlighting the values, you can simply create a new Text on top of the searchBar that contains the closest match. I suggest you read this article on filtering data in react native: https://www.freecodecamp.org/news/how-to-make-a-filter-component-in-react/
TextInput for IOS - https://snack.expo.dev/JrWw1Zj5Z
TextInput for android - https://snack.expo.dev/piRXBQv6aM
For better view you can disable borders on TextInput or make TextInput transparent, and set borders and other styles to Text component

Passing state via react navigation

UPDATE
OK, so I got a bit further. I made it work, but it's ugly and buggy :)
What I want to do - I'm trying to share a search field between two screens. Almost identical to the way Yelp works, actually. The first screen is the map with the search field on top. When you click the search field, it should move into the second search screen where you type the search term or select some default search category.
I have an expo app with bottom tab navigator (TS). Let me paste only the important parts:
export default function BottomTabNavigator() {
return (
<BottomTab.Navigator initialRouteName="Search">
<BottomTab.Screen
name="Search"
component={SearchNavigator}
...
and then
const SearchStack = createStackNavigator<SearchParamList>();
function SearchNavigator() {
return (
<SearchStack.Navigator headerMode="none">
<SearchStack.Screen name="SearchScreen" component={SearchScreen} />
<SearchStack.Screen name="SearchFocusScreen" component={SearchFocusScreen} />
</SearchStack.Navigator>
);
}
SearchScreen
const [searchQuery, setSearchQuery] = useState('');
// If we returned from the second search screen with a search term, set it in state.
useEffect(() => {
if (route.params) {
setSearchQuery(route.params.searchTerm);
}
}, [route]);
const searchRef = useRef(null);
const onFocus = () => {
// If we have a search term, pass it to screen 2, then blur the input so we don't loop back.
if (route.params) {
setSearchQuery(route.params.searchTerm);
navigation.navigate('SearchFocusScreen', { searchTerm: searchQuery });
} else {
navigation.navigate('SearchFocusScreen');
}
searchRef.current.blur();
};
return (
<View>
<MapView ... />
<View>
<SafeAreaView>
<Searchbar
ref={searchRef}
placeholder="Search"
value={searchQuery}
onSubmitEditing={() => {
initSearch({ searchQuery, region });
}}
onFocus={onFocus}
/>
Second search screen
const [searchQuery, setSearchQuery] = useState('');
const searchRef = useRef(null);
useEffect(() => {
searchRef.current.focus();
if (route.params) {
setSearchQuery(route.params.searchTerm);
}
}, [route]);
return (
<SafeAreaView>
<View>
<Searchbar
ref={searchRef}
placeholder="Search"
onChangeText={(query) => setSearchQuery(query)}
value={searchQuery}
onSubmitEditing={() => {
navigation.navigate('SearchScreen', { searchTerm: searchQuery }); // Pass the search query back to Search page 1
}}
...
Does it work? Yes. But it feels wrong. Also - there's an issue that when I type with an error into the search field in search screen 2, I get auto-correct suggestions. If I then quickly click Search, it auto corrects the typo after sending the error to search screen 1. And it's visible.
If you guys have a better strategy to achieve this, please do share.
Because navigate is an object, useEffect only compares the reference value. If you would like changes to that object to trigger a useEffect, I would look at https://github.com/kentcdodds/use-deep-compare-effect

Recursion: How to fix error "Invariant Violation: Text strings must be rendered within a <Text> component."

I am trying to write a recursive function to display a discussion (conversation and replies) tree.
export default class SocialPost extends Component {
constructor(props) {
super(props);
}
replies = data => {
return data.map(item => {
return <View key={item._id}>{this.individualPost(item)}</View>;
});
};
individualPost = data => {
return (
<View>
<View style={styles.container}>
<Text>{data.comment}</Text>
</View>
{data.replies.length && this.replies(data.replies)}
</View>
);
};
render() {
return <View>{this.individualPost(this.props.data)}</View>;
}
}
data = [
{
replies: [
{
replies: [],
_id: "5cb07bb28346d729a25dfc38",
comment: "xyz"
}
],
_id: "5cb07b8a8346d729a25dfc37",
comment: "abc"
}
];
but I get this error instead: Invariant Violation: Text strings must be rendered within a component.
What can I do to fix this problem?
The problem is probably on this line: {data.replies.length && this.replies(data.replies)}.
If data.replies.length is 0, the part after the && will not be called. That means that React will attempt to render the string "0" there, which is not allowed outside of a <Text> component.
A solution would be to just do {this.replies(data.replies)}, and then return null from the replies() function when there are no replies.
data.comment doesn't exist on your first pass. Data is just an array, so react-native things you are trying to render something here w/o it existing. The error is telling you literally that only text can be rendered there. You are trying to render a data type.
individualPost = data => {
return (
<View>
<View style={styles.container}>
<Text>{data.comment}</Text> //your problem
</View>
{data.replies.length && this.replies(data.replies)}
</View>
);
};