React Native + React I18next + React Navigation, navigationOptions update issue - javascript

I'm using react-i18next and react-navigation.
Currently I wrap all my components with withNamespaces individually, when there's a need. The issue is that I can't keep the title in static navigationOptions up to date. It just doesn't update, no matter how I assign it: as a function or as properties object. The navigation.setParams does not update it as well.
I tried using withNamespaces on Navigators themselves to make use of screenProps, as it's done here, but in this case my dispatched NavigationActions have no effect. The navigation just doesn't happen.
I assume that i18next HOC somehow prevents its children from receiving params update events. Do I need to initialise the i18next in some other way to resolve this? Or is there a way to force the title in navigationOptions to update?

Ok, I came up with a simple way to solve this. I made a component that just returns the required string, and I wrapped it with withNamespaces and put it into title prop of navigationProperties. Works fine.
Here's an example code.
Title Component
import React from 'react'
import PropTypes from 'prop-types'
import { withNamespaces } from 'react-i18next'
import { Text } from 'react-native'
const ScreenTitle = ({ path, t }) => <Text>{t(path)}</Text>
ScreenTitle.propTypes = {
path: PropTypes.string.isRequired,
t: PropTypes.func.isRequired,
}
export default withNamespaces()(ScreenTitle)
Usage
static navigationOptions = () => {
return {
title: <ScreenTitle path="privacyPolicy:title" />,
}
}

Related

React issue applying styles when converting existing component from class-based to function-based

I am having issues seeing styles getting applied from my component when converting my component from class-based to function-based component.
it is probably related to HOC issue.
So in my component, I have the styles defined as below;
const styles = ({palette: {somekit}}) => ({
tabs: {
backgroundColor: somekit.grey
},
})
Earlier, it was a class-based component as below (and I could see the styles getting picked from above)
class MyHeader extends React.Component{}
However, after converting it into a function-based component as below, the styles defined above do not seem to be getting picked up.
function MyHeader(props) {}
In both the cases, I had the export defined as below;
export default withRouter(withStyles(styles, {name: 'MyStyle'})(MyHeader));
I suspect some issue with the way the component is exported above when converting from class-based to function-based.
Does this export seem proper when using function-based component ?
---Updated---
This is how withStyles is defined;
import { withStyles as withStylesWODefault } from '#material-ui/styles';
import defTheme from './defTheme';
export default function withStyles(styles, options) {
return withStylesWODefault(styles, {
defTheme,
...options
});
}

props.images.map is not a function (function component)

Currently this two components are child components, so what I want to achieve here is to pass the props from Search component to Images component. Because I wrote everything in a function component, I thought this might be a great place for me
When user enters something in the input field it will automatically search for new images. In order to give you a better understanding of my issue, I will put a link to Codesandbox here. So I want to pass the props to the sibling component, so it will output the filtered search result.
https://codesandbox.io/s/patient-dawn-khouk
So only files you need to look at are: Search.js, Images.js
Ensure your images prop is actually an array. Can you do some debugging to figure out what your images object actually is?
Using propTypes is a way to have react help, and if it is nullable (i.e. not isRequired), then use a guard on it:
import React from "react";
import PropTypes from 'prop-types';
import Image from "./Image";
const propTypes = {
images: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired, // used as key
src: PropTypes.string.isRequired, // used for image source
<... other object properties ...>
})
),
};
const Images = ({ images }) => {
const images = images && images.map((image) => {
return <Image key={image.id} image={image}/>
});
return <div className="images">{images}</div>;
}
Images.propTypes = propTypes;
export default Images;

Is it possible to create a React component interface?

I have the following react component:
class Cmp extends React.Component {
render () {
return <h3>{this.props.title}</h3>;
}
}
But I would like to expose or say to the consumer of my component to use it with a title otherwise it does not work the component.
Consumer would use it like
<Cmp title='Some fancy title' />
I need the consumer of my component to know that he should provide a title otherwise the component does not have any sense.
You can use PropTypes and set it to isRequired. You can also check if the prop is set at componentWillReceiveProps() and throw your error.
If you return null from a render method, nothing is rendered. You could use this knowledge to conditionally check if the prop is passed, and return null if the prop is not passed. The advantage here over using componentWillReceiveProps() is that you could use a functional component rather than a class component.
In rare cases you might want a component to hide itself even though it
was rendered by another component. To do this return null instead of
its render output.
Preventing Component from Rendering
Realistically you would also use PropTypes.
Cmp.propTypes = {
title: PropTypes.string.isRequired
};
Short Example
import React from 'react';
import PropTypes from 'prop-types';
const Cmp = (props) => props.title ? <h3>{props.title}</h3> : null
Cmp.propTypes = {
title: PropTypes.string.isRequired
}
export default Cmp;

Refresh contentComponent in react-navigation

I am using React-Navigation where I am using functionality of custom drawer by using contentComponent of React-Navigation.
const DrawerNavigation = DrawerNavigator({
DrawerStack: { screen: DrawerStack }
}, {
contentComponent: DrawerComponent,
drawerWidth: 300
})
Here DrawerComponent is my custom navigation drawer where I have used custom navigation items like username, profile picture, email address and other menus.
Now whenever user updates their profile I want to refresh my DrawerComponent, I am not able to find any way to do it. Can anybody suggest me a good way to implement this?
Couple of options here, and all are tight to how you want to achieve your state management.
First, one solution would be to have the your user state in the component creating the DrawerNavigator, and pass it down to your custom drawer component. This presents the disadvantage of having to recreate your navigator on state change and create a blink. I do not advice to use this solution but it's worth mentioning as a possibility.
You could also use a React Context, have your user state in a top level component, create a provider passing it the user as the value and make your drawer a consumer of this context. This way, every time the user changes your drawer component would re-render.
What I use personally is Redux to connect my Drawer directly to my global state. It involves a bit of setup but it's worth it in the end. A root component could look like this:
import React from 'react'
import { Provider } from 'react-redux'
export default () => (
<Provider store={store}>
<App />
</Provider>
)
Where store is the result of:
import { createStore, combineReducers } from 'redux'
import reducers from './reducers'
const store = createStore(combineReducers(reducers))
Your reducers are going to be the state of your app, and one would be dedicated to your user data.
Then your Drawer component could be:
import React, { Component } from 'react'
import { View, Text } from 'react-native'
import { connect } from 'react-redux'
#connect(({ user }) => ({ user }))
class Drawer extends Component {
render () {
const { user } = this.props
return (
<View>
<Text>My name is {user.name}</Text>
</View>
)
}
}
export default Drawer
Now, every time you change your user reducer, this Drawer component will re-render.
There is a few things your should know about Redux, so you should probably read up a bit the Getting Started docs.
I know it is a old question now but you can do this by importing the code like
import DrawerView from '../Drawer/Drawer'
contentComponent: DrawerView
then in the DrawerView file
class DrawerView extends Component {
render(){
return(
//Do your stuff here
)
}
}
export default DrawerView;
for more info please visit this link and thank to Kakul Gupta for this https://codeburst.io/custom-drawer-using-react-navigation-80abbab489f7
The easiest way to change menus without using redux is, using createSwitchNavigator.
https://reactnavigation.org/docs/en/auth-flow.html

Using browserHistory.push to change Route dynamically doesn't work with react-router v4

I try to do a simple redirect in a function...
I tried this:
Router.browserHistory.push('/dashboard');
But then I got this error:
Uncaught TypeError: Cannot read property 'push' of undefined
whats my fail?
Creating a new browserHistory won't work because <BrowserRouter> creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>.
browserHistory is not available form react-router-dom package from v4 , and is separated to history package.
Navigating with WithRouter
You can rather make use of withRouter Higher Order Component and navigate with history prop
From the official documentation
You can get access to the history object’s properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will re-render its component every time the route changes with the same props as <Route> render props: { match, location, history }.
Sample snippet
import {withRouter} from "react-router";
class MyComponent extends React.Component {
...
changeRoute = () => {
this.props.history.push('/dashboard)
}
...
}
export default withRouter(MyComponent);
Also see this answer for more info on nesting and dynamically routing in react-router-v4
Nesting routes and dynamically routing in React-router v4
As mentioned in the comments you should use this new approach for v4 react router but if you are looking for a quick workaround you can use context.
```javascript
import { PropTypes } from 'prop-types'
import React from 'react'
export default class MyComponent extends React.Component {
...
// redirect to dashboard
redirectToDashboard = () => {
this.context.router.history.push('/dashboard');
}
}
MyComponent.contextTypes = {
router: PropTypes.object
}
This should do the trick

Categories