I'm developing a react-native app and I want to multi language support. I also wrote languageHelper to be easy to manage.
languageHelper.js
import AsyncStorage from '#react-native-async-storage/async-storage';
export const Translator = async (key) => {
try {
const langCode = await AsyncStorage.getItem('#lang_code')
return I18n.t(key, { locale: langCode ?? 'en' })
} catch (e) {
return I18n.t(key)
}
}
login.js
render() {
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<KeyboardAvoidingView behavior={'height'} style={styles.container}>
<ScrollView>
<ImageBackground source={require('../Sources/LoginBg.jpg')} style={styles.backgroundImage}>
<Layout style={styles.header}>
<Text category='h1' status='primary'> {Translator('Login_hello')}</Text>
<Text category='h6' status='primary'> {Translator('Login_signInMessage')}</Text>
</Layout>
Error I encountered
Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead.
But since Translator is a async func so I can't use in render. (I tried to async render).
How can I solve this? Thanks for helping.
Well this seems like a not so performant code, but if you want to make it work anyways you could return an empty string while your translation is working on it, something like this:
import AsyncStorage from '#react-native-async-storage/async-storage';
export const Translator = React.memo(({key}) => {
const [text, setText] = useState('')
React.useEffect(() => {
translate()
}, [key])
const translate = async () => {
try {
const langCode = await AsyncStorage.getItem('#lang_code')
setText(I18n.t(key, { locale: langCode ?? 'en' }))
} catch (e) {
setText(I18n.t(key))
}
}
return (
<Text>{text}</Text>
)
})
and you could use it like this in you code (using your example):
render() {
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<KeyboardAvoidingView behavior={'height'} style={styles.container}>
<ScrollView>
<ImageBackground source={require('../Sources/LoginBg.jpg')} style={styles.backgroundImage}>
<Layout style={styles.header}>
<Translator('Login_hello')} />
<Translator('Login_signInMessage')} />
</Layout>
You also can make you translator accept styling props to customize the end result. Also really important to use the memo there so you don't have extra renders on your translate component as it always request from async.
Other solution
The solution I would actually recommend is to create a translation provider and configure the i18n globally there so you'll have only one storage request and your translate component will be really less expensive to load.
Success on your project.
Related
I am trying to implement deeplinks in my app using firestore's dynamic links. When the app is opened using a deeplink, I want to show a modal to the user. I am new to react and I am not sure how to implement this. When the app is opened using the dynamic link, I can get the link in App.js
useEffect(() => {
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
return () => unsubscribe();
}, []);
const handleDynamicLink = link => {
//Show Modal
};
return (
<RootStoreProvider value={rootStore}>
<SafeAreaProvider initialSafeAreaInsets={initialWindowSafeAreaInsets}>
<IconRegistry icons={EvaIconsPack} />
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<ApplicationProvider {...eva} theme={eva[theme]}>
{!rootStore.authStore.isLoggedIn && !startedPressed ? <WelcomeSliderScreen pressed={getStartedPressed}></WelcomeSliderScreen> :
<RootNavigator
ref={navigationRef}
initialState={initialNavigationState}
onStateChange={onNavigationStateChange}
/>
}
</ApplicationProvider>
</ThemeContext.Provider>
</SafeAreaProvider>
</RootStoreProvider>
)
}
What is the best way to implement this logic? I started to implement a deepLinkStore but now a start the think that this is not the best solution
const handleDynamicLink = link => {
rootStore && rootStore.linkStore.setDeepLink(link);
};
The problem is that rootStore is sometimes null there, I don't understand why.
Shall I just provide a parameter in RootNavigator? Something like this
const [deepLink, setDeepLink] = React.useState(null)
const handleDynamicLink = (link) => {
setDeepLink(link);
}
<RootNavigator
ref={navigationRef}
initialState={initialNavigationState}
onStateChange={onNavigationStateChange}
deepLink={deepLink}
/>
Is this the way to go? How can I access the deepLink parameter in a functional component?
I am attempting to have an icon switch its visual when clicked (like a checkbox). Normally in react native I would do something like this:
const [checkbox, setCheckbox] = React.useState(false);
...
<TouchableHighlight underlayColor="transparent" onPress={() => {setCheckbox(!setCheckbox)}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
However I have made some changes, and now I can't seem to replicate this behavior. I am using AsyncStorage class to storage and get arrays of objects for display. For simplification, in the example below I removed the storage code, and the objects each have an 'id' and an 'added' attribute, which is essentially the boolean value of the checkbox.
I am now attempting to update the icon shown to the user whenever it is pressed. I know the function is being called, but it will not update the icon. I am using array.map to create the list of icons. I created a demo here, and the code is below: https://snack.expo.dev/#figbar/array-map-icon-update
const templateObject = {
id: 0,
added: false,
};
const templateObject2 = {
id: 1,
added: true,
};
export default function App() {
const [savedNumbers, setSavedNumbers] = React.useState([]);
React.useEffect(() => {
setSavedNumbers([templateObject,templateObject2]);
}, []);
const populateSavedNumbers = () =>
savedNumbers.map((num, index) => <View key={index}>{renderPanel(num.id,num.added)}</View>);
const updateNumber = (id) => {
let capturedIndex = -1;
for(var i = 0; i < savedNumbers.length; i += 1) {
if(savedNumbers[i].id === id) {
capturedIndex = i;
break;
}
}
let _tempArray = savedNumbers;
_tempArray[capturedIndex].added = !_tempArray[capturedIndex].added;
setSavedNumbers(_tempArray);
}
const renderPanel = (id:number, added:boolean) => {
return (
<View>
<TouchableHighlight underlayColor="transparent" onPress={() => {updateNumber(id);}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
</View>
);
}
return (
<View>
<View>buttons:</View>
<View>{populateSavedNumbers()}</View>
</View>
);
}
This is a common React pitfall where things don't re-render when it seems like they should. React does shallow comparisons between new and old states to decide whether or not to trigger a re-render. This means that, when declaring a variable to simply equal a state variable which is an object or an array, a re-render is not triggered since those two variables now reference the same underlying data structure.
In this case, you are setting _tempArray to reference the array savedNumbers rather than creating a new array. Therefore, React's shallow comparison comes back as "equal", and it doesn't believe that a re-render is necessary.
To fix this, change this line:
let _tempArray = savedNumbers;
to this:
let _tempArray = [...savedNumbers];
Hi i am making an app in react native and i have a problem
let currentUserUID = firebase.auth().currentUser.uid;
const [partnership, setPartnership] = useState('');
useEffect(() => {
async function getUserInfo(){
let doc = await firebase
.firestore()
.collection('users')
.doc(currentUserUID)
.get();
let dataObj = doc.data();
setPartnership(dataObj.partnership)
}
getUserInfo();
})
console.log(partnership)
return(
<View style={{flex: 1}}>
{partnership === "Partner" || "1KPartner" || "BusinessPartner" &&
<Text>You have permission</Text>
}
<LessonHeader navigation={navigation}/>
<StyledBoxV3 style={{flex: 1}}>
<LessonMenu item={item} navigation={navigation} />
</StyledBoxV3>
</View>
)
};
const StyledBoxV3 = styled(StyledBox)`
padding: 0;
border-top-left-radius:0;
border-top-right-radius:0 ;
`
so when someone is Partner, 1KPartner or BusinessPartner i want to show or hide specific data but i get the error
"Error: Text strings must be rendered within a component."
The problem is in de conditional logic, if i delete it i dont have any problem. How can i make this work? I already tried to put a variable in my return and update it with a function in my useEffect but it doesn't work for me.
Your logic where you are comparing the value of the partnership is written wrong. I would suggest being classy and writing
{(partnership === "Partner" ||
partnership === "1KPartner" ||
partnership === "BusinessPartner") &&
<Text>You have permission</Text>}
Try updating the condition like this,
A helper function to determine the component to render based on the partnership state.
const renderPermissionText = (value: String) => {
switch (value) {
case 'Partner':
case '1KPartner':
case 'BusinessPartner':
return <Text>You have permission</Text>;
default:
return null;
}
};
Update the Layout to
...
<View style={{flex: 1}}>
{renderPermissionText(partnership)}
...
</View>
I am working on developing a program that requires CRUD operations using Firebase.
I am using Firestore to store my data, and I have a collection called "notifications" that looks like this:
https://i.stack.imgur.com/hCe8q.png
Now, what I would like to do is to use a Flatlist to display on the screen different notifications a user receives. I would like to do this such that it updates in realtime whenever a document is added/removed/modified into the collection "notifications".
I am going based off of this example:
https://rnfirebase.io/firestore/usage-with-flatlists
I keep running into this error however:
Error: Requiring module "node_modules/#react-native-firebase/firestore/lib/index.js", which threw an exception: Invariant Violation: Native module cannot be null.
(Much longer error but I'm pasting the main part)
If anyone could please help me with this I would greatly appreciate it. Thank you.
My code is below:
import { ActivityIndicator, FlatList, Text, SafeAreaView } from "react-native";
import firestore from "#react-native-firebase/firestore";
export default function NotificationScreen() {
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [notifications, setNotifications] = useState([]); // Initial empty array of users
useEffect(() => {
const subscriber = firestore()
.collection("notifications")
.onSnapshot.forEach((querySnapshot) => {
const notifications = [];
querySnapshot.forEach((documentSnapshot) => {
notifications.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setNotifications(notifications);
setLoading(false);
});
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
data={notifications}
renderItem={({ item }) => (
<SafeAreaView
style={{
flex: 1,
}}
>
<Text>Notification ID: {item.id}</Text>
<Text>Notification time: {item.time}</Text>
</SafeAreaView>
)}
/>
);
}```
[1]: https://i.stack.imgur.com/Rhpgv.png
I really need some help with hooks in react native...
I have an ActionSheet which onPress one of the options a camera feature should open with "useCamera" function.
The "useCamera" is in another function(on another file),
using the library "ImagePicker". (specific "ImagePicker.launchCamera")
the "ImagePicker.launchCamera" set the fileUri in the callback while the function return nothing.
So to access the fileUri, I tried to use useState Hook but I get "ERROR: Invalid Hook call.Hooks can only be called inside of the body of the function..."
but the hook inside the function bodY!
ActionSheet code:
import {useCamera} from './PhotoHandle';
// export default class EditProfilePic extends React.Component {
function EditProfilePic(props) {
const [ActionSheetRef, setActionSheetRef] = useState(null);
const [fileDate, setFileDate] = useState(null);
return (
<View style={styles.images_container}>
<TouchableOpacity
style={styles.click_edit_icon}
onPress={() => ActionSheetRef.show()}>
<Image style={styles.editIcon} source={editIcon} />
</TouchableOpacity>
<Image source={props.profileSource} style={styles.image} />
<ActionSheet
ref={o => setActionSheetRef(o)}
title={
<Text style={{color: '#000', fontSize: 18}}>
Which one do you like?
</Text>
}
options={['Select from Photos', 'Camera', 'Cancel']}
cancelButtonIndex={2}
destructiveButtonIndex={2}
onPress={index => {
//camera
if (index == 1) {
useCamera()
}
}}
/>
</View>
);
}
LaunchCamera function:
export function useCamera() {
const [fileData, setFileData] = useState(null);
let options = {
storageOptions: {
skipBackup: true,
path: 'images',
},
};
ImagePicker.launchCamera(options, response => {
console.log('coolio' + response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
alert(response.customButton);
} else {
setFileData(response.data);
}
});
return fileData;
}
I tried to call useCamera() like "< useCamera / >",
I changed the anonymous function to just a function.
I tried to call useCamera() from other part of the code (not from onPress) .
I tried to use Context,and Redux.
ALL THE SAME ERROR.
please help meee,
Thanks!
I've had this problem before as well what version of React-Native are you using?
React 16.8.0 is the first release to support Hooks. When upgrading, don’t forget to update all packages, including React DOM. React Native supports Hooks since the 0.59 release of React Native.
https://reactjs.org/docs/hooks-intro.html
I see the sign, you call this.setState, it means you are using a class-based component.
a react hook can be used only in a functional component.
You have to convert the component which called LaunchCamera() (it should be renamed useCamera()) to the functional component.