React Native Arabic (RTL) without forceRTL - react-native

In RN my bilingual app (English - Arabic), I have used I18nManager (views) and I18n (for translations)
When I am changing app language to Arabic, the whole app gets reloaded again from the splash-screen using this code:
I18nManager.forceRTL(true)
Ideally, it should not restart the app from start and it should continue with the current screen with Arabic data.
Currently, it is not happening, only translation elements are getting converted using I18n.t('keyword') but for views Arabic alignment, it's not proper.
Still looking for a better solution, let me know if anyone achieved it.
Thanks
Sopo !!

you should put this code in the top component in your project
import RNRestart from "react-native-restart";
I18nManager.forceRTL(true);
if (!I18nManager.isRTL) RNRestart.Restart();

If you guys wants to store stack state after reloading(because there is no other option without reloading) and want stack state back you can follow this link also you can check my code.
Link: React navigation state persist
Any Component
AsyncStorage.setItem('navigation_state', JSON.stringify(navigation.dangerouslyGetState()));
My App.js
const App = () => {
const [initialState, setInitialState] = useState();
const [isReady, setIsReady] = useState(false);
useEffect(() => {
restoreState();
}, []);
const restoreState = async () => {
try {
const savedStateString = await AsyncStorage.getItem('navigation_state');
const state = savedStateString ? JSON.parse(savedStateString) : undefined;
if (state !== undefined) {
AsyncStorage.removeItem('navigation_state');
setInitialState(state);
}
} finally {
setIsReady(true);
}
};
if (!isReady) {
return null;
}
return (
<Provider store={store}>
<NavigationContainer
initialState={initialState}
ref={rootNavigationRef}>
<Root>
<AppNavigator />
</Root>
</NavigationContainer>
</Provider>
);
};

I working on a project which has two languages, Arabic and English.i use redux for handling app language. I put all styles on redux and handle app style with redux. and when user change language all styles on my app change to that language . also all text handled with redux too. with this way, my app does not reload and app language changed immediately.

If your app is an android hybrid app, you can try this:
import com.facebook.react.modules.i18nmanager.I18nUtil;
I18nUtil i18nUtil = I18nUtil.getInstance();
i18nUtil.forceRTL(context, forceRtl);
i18nUtil.allowRTL(context, true);
value 'forceRtl' is a boolean.
for iOS,I think you can find the same method.

In Expo use
import {Updates} from "expo"
Updates.reload()

Related

apollo-client: makeVar / useReactiveVar can be updated from another screen?

I use react-native with apollo-client3.
In my App.js file that starts my project, I configure if I log in go to LoggedInNav else LoggedOutNav.
<ApolloProvider client={client}>
<NavigationContainer>
<View onLayout={onLayoutRootView}></View>
{isLoggedIn ? <LoggedInNav /> : <LoggedoutNav />}
</NavigationContainer>
</ApolloProvider>
And the state whether I log in or not is tracked from useReactiveVar.
const isLoggedIn = useReactiveVar(isLoggedInVar);
In my LogIn.js file, I do log in and if it succeeds, I update my isLoggedInVar state made from makeVar of apollo/client from false to true.
export const isLoggedInVar = makeVar(false);
export const tokenVar = makeVar("");
export const logUserIn = async (token) => {
try {
await AsyncStorage.setItem("token", JSON.stringify(token));
isLoggedInVar(true);
tokenVar(token);
} catch (e) {
console.error(e);
}
};
So I update my state of makeVar in another screen, but can App.js listen to this update and navigate from <LoggedInNav/> to <LoggedOutNav/>?
I think App.js file is the bottom of all screens of my app, it should work.
But in my app project, I feel sometimes it might works but sometimes it throws Network error.
And in this case How can I make switch between two Navs according to Login state? Please help me.

Access a value in the redux store in the App.js

I'm using react-admin. I would need to be able to access a value in the redux store in the App.js file. Based on this value, I pass different props to the Resources components included in Admin.
If I access the store in a classic way I get an error like this:
could not find react-redux context value; please ensure the component is wrapped in a
This is normal because the App is not wrapped by the provider. But do I have a way to do it by leaning on another component? Or some other way?
Thank you
const App = () => {
const theme = useSelector(state => state.theme);
useEffect(() => {
loadReCaptcha();
console.log('theme', theme);
}, []);
return (
<Admin
title="Atena"
dataProvider={dataProvider}
customReducers={{ theme: themeReducer }}
....

Condition rendering failing in React Native Redux App

I'm trying to conditionally render my redux app based on if the user is logged in. The relevant & condensed version of my code is below:
let isLoggedIn = false;
export default function App() {
console.log('App executing...');
console.log('isLoggedIn: ', isLoggedIn);
return (
<Provider store={store}>
<NavigationContainer>
{isLoggedIn ? ContactsTab() : Login()}
</NavigationContainer>
</Provider>
);
}
store.subscribe(() => {
// Set isLoggedIn to true if token is received and reinvoke App()
if (store.getState().user.token) {
isLoggedIn = true;
App();
}
});
The app starts with console logging isLoggedIn: false and displaying Login()(as expected). When I login on my phone using the correct credentials, App() is re-invoked console logging isLoggedIn: true(as expected) but it's still displaying Login(). If I set isLoggedIn = true inside the app function, the app successfully starts displaying the ContactsTab().
What is happening here? Why is my app not moving to ContactsTab() when the value of isLoggedIn successfully changes to true? How can I fix this?
Thank you for reading along. I have been trying to debug this for the past 2 days with no success so any help would be greatly appreciated!
You need to use useState here like this, the useState will automatically renders when the state changes
export default function App() {
const [isLoggedIn, setLoggedIn] = useState(false);
console.log('App Executing...');
console.log('isLoggedIn: ', isLoggedIn);
store.subscribe(() => {
// Set isLoggedIn to true if token is received and reinvoke App()
if (store.getState().user.token) {
setLoggedIn(true);
}
});
return (
<Provider store={store}>
<NavigationContainer>
{isLoggedIn ? ContactsTab() : Login()}
</NavigationContainer>
</Provider>
);
}
Hope this helps!
What is happening here? Why is my app not moving to ContactsTab() when the value of isLoggedIn successfully changes to true? How can I fix this?
Re-invoking app doesn't necessarily re-renders your screen. Your conditional invoking is not working properly because your render method is only called once, to fix it you need to change the state of your App component. You are just changing the state through your reducer but you are not listening to that change in your app component. You must listen to that change and upon that change, you need to set login state to true and then your component will perform rendering for you.
Read more about state here.
Read more about how you can use redux to make your components listen to the change in state of your application here.

Force change app language without restart

Currently my react native app allow language switching by user (have a language selection screen in the app for user to switch) with react-native-localization. What i did is use redux-persist to store the selected language. But that requires an app restart to take effect.
However, we have a business requirement to switch the language on the fly. Anyone have successfully implement it before?
import LocalizedStrings from 'react-native-localization';
export default new LocalizedStrings({
en: {
},
de: {
},
});
In my main navigator:
componentDidMount() {
const language = store.getState().language;
if (language.code) {
LocalizedStrings.setLanguage(language.code);
} else {
LocalizedStrings.setLanguage('en');
}
}
Anyone have successfully implemented in such a way that an app restart is not required?
When language got update, you just need to update key of the root component to force all child component re-render.
In my case, I am using react-navigation, whenever language changed. I update screenProps
export default () => {
const language = useSelector((state: AppState) => state.preferences.language);
return (
<AppContainer
screenProps={{ language }}
...
/>
);
};

React navigation state management without redux

I'm using React Navigation library for my React Native project and struggling to understand how to handle state with it.
In normal React Native application I can have state at the top level component and pop events from child components via props, however with React Navigation it seems that I cannot pass any props to components used as Screens.
After reading through related GitHub issue it seems that library devs are very opinionated in forcing everyone to use some kind of global event handler - redux or mobx, I guess.
The handler which needs to modify the following state. I got stuck when I started to try to move the state inside the app as I couldn't figure out how to:
Pass the handler to the TaskForm component.
Pass the state as props to TaskList if its rendered as part of App.js
Please, avoid replying "just use redux". I believe that using redux in this example would be massive overkill.
I use react native and react navigation in my app without redux, and so far it’s working great. The trick is passing screenProps all the way down the line.
For example, I have a More view. I create a basic More view with 2 sub views in a stack:
class More extends Component {
render() {
return <something>
}
}
class SubView1 extends Component {...}
class SubView2 extends Component {...}
Then I create the stack:
const MoreStack = StackNavigator({
More: {
screen: More
},
SubView1: {
screen: SubView1,
},
...
}, options);
And then I create a wrapper class that I export:
export default class MoreExport extends Component {
static navigationOptions = {
title: "More"
}
render() {
return <MoreStack screenProps={this.props.screenProps} />;
}
}
If all of this is in More.js, I can just import More from More.js and use it anywhere else.
If I then pass in screenProps to my root view and then have a wrapper class for each layer, I can pass the screenProps all the way down, and all views can access them using this.props.screenProps.
I use a wrapper like the one above around each StackNavigator and TabNavigator, and the screenProps are passed all the way down.
For example, in my root class’s render method, I could do:
return <More screenProps={{prop1: something, prop2: somethingElse}} />
And then the More class and each SubView in the MoreStack would all have access to these props.
Feel free to let me know if you want more clarification!
Disclaimer: I don’t know if this is the correct or recommended way to do it, but it does work
You can set param to navigation like this:
static navigationOptions = ({ navigation }) => {
return {
tabBarIcon: ({ tintColor, focused }) =>
<View>
<Icon name="bell-ring" size={24} color={focused ? 'green' : 'black'} />
{(navigation.state.params.badgeCount && navigation.state.params.badgeCount > 0) ?
<Text>{navigation.state.params.badgeCount}</Text>
:
<View></View>
}
</View>
}
}
and change badgeCount by:
this.props.navigation.setParams({ badgeCount: 3 })
After being inspired by steffeydev I looked more around react community issues and found a very simple example of wrapper using function.
It's surprisingly simple solution and I don't know why I didn't think about it before.
The function is the following:
const createComponent = (instance, props) =>
navProps => React.createElement(instance, Object.assign({}, props, navProps));
Thanks for inspiration and pointing me to screenProps which lead me to finding this solution.
I was struggling with the same issue and tried some of the other answers before discovering the following part of the documentation for React Navigation: https://reactnavigation.org/docs/en/stack-navigator.html#params.
Essentially, each Screen in the Stack can be passed params which can include handlers and then the various screens can interact with the application state.
My general structure is then to have an App class with state and handlers and the handlers are then passed into each Navigation Screen as needed. I'm not sure if I have this pattern right, but it's the way I understood the general React tutorial.
Example: In my demo app, I have a page flow like this:
Park finder screen -> Park detail screen (with Bookmark action)
Bookmark list screen -> Park detail screen
If you find a park, you can click on a Bookmark button which adds the park to the list of bookmarks shown on the Bookmark screen. You can then click on a park bookmark to see the details.
My App looks generally like this:
import React, { Component } from 'react';
// Screens
import ParkFinderScreen from './Components/ParkFinderScreen';
import ParkBookmarksScreen from './Components/ParkBookmarksScreen';
import ParkDetailsScreen from './Components/ParkDetailsScreen';
// Navigation
import { createStackNavigator, createBottomTabNavigator, createAppContainer } from 'react-navigation';
class App extends Component {
constructor(props) {
super(props);
this.state = {
bookmarks: new Map()
};
}
// Bookmark the park shown in the detail section.
handleBookmark (park) {
let newBookmarks = this.state.bookmarks;
newBookmarks.set(park.parkCode, park);
this.setState({
bookmarks: newBookmarks
});
}
render() {
const FinderStack = createStackNavigator(
{
ParkFinder: {
screen: ParkFinderScreen
},
ParkFinderDetails: {
screen: ParkDetailsScreen,
params: {
handleBookmark: (park) => this.handleBookmark(park),
}
},
}
);
const BookmarksStack = createStackNavigator(
{
ParkBookmarks: {
screen: ParkBookmarksScreen,
params: {
bookmarks: this.state.bookmarks
}
},
ParkBookmarksDetails: {
screen: ParkDetailsScreen,
},
}
);
const AppNavigator = createBottomTabNavigator(
{
Bookmarks: BookmarksStack,
Finder: FinderStack,
}
);
const AppContainer = createAppContainer(AppNavigator);
return (
<AppContainer/>
);
}
}
export default App;
I'm using Apollo Client, but I've removed those parts.
In the Screen components, you can access the props like other ones using this.props.navigation.getParam('bookmarks').
One issue I encountered was that whenever I change the App state, I'm taken to the first screen. The state is updated, but it's a little disorienting. I'm not sure if there is a way to update the App state while staying on the screen. I can sort of understand that given the App state has updated, all the children need to be updated and so the current screen (which is part of a child I think) is reset. I don't know if that is a limitation of the system or a byproduct of how I designed the components.
I hope this helps someone. This seems to keep with the intended behavior of React Native. The library team really seems to want you to not use Redux. https://reactnavigation.org/docs/en/redux-integration.html