Need help on some concepts about ReactNative and Redux and Navigator - reactjs

After several tests in this scenario, I have some questions that I can not answer my self, so I ask for help to clarify my concepts.
Provider vs props in Navigator
What is the difference and what is the best approach to setup the Navigator and pass store to different Scenes of a React Native app
export default class App extends Component {
render () {
return (
<Provider store={store}> //<-- here
<Navigator style={{flex: 1}}
initialRoute={{ component: MainContainer }} //<-- here
renderScene={ (route, navigator) => {
const Component = route.component;
return (
<View style={{flex: 1, marginTop:40}}>
<Component navigator={navigator} route={route} {...route.passProps} />
</View>
...
MainContainer is connected with Component within react-redux connect function to pass Props and Actions to props.
Is it better access to context or to props?
vs
const store = createStoreWithMiddleware(reducer, initialState); //<-- here
export default class App extends Component {
render () {
return (
<Navigator style={{flex: 1}}
initialRoute={{ component: MainComponent }}
renderScene={ (route, navigator) => {
const Component = route.component;
return (
<View style={{flex: 1, marginTop:40}}>
<Component
navigator={navigator}
route={route}
{...route.passProps}
store={store} //<-- here
/>
</View>
...
In a component Scene, (not wrapping as a smart container) how to setup a listener about changes in redux state or have i to bind component state to redux state.
Passing state (of Redux store) and actions as passProps when pushing newScene in Navigator, and in then newScene dispatch actions and executed correctly, the state is update, but... does not re-render the scene.
Do I have to bind state component to Redux state to see the changes reflected on screen?
Is there any sample about best practices in this scenario?
props vs connect in the same Scene
In the same Scene, from Top to Down components, which is the best approach to pass redux state (not talking about component state) as wrapping a component in a 'smart' container with 'connect' from react-redux, or passing the hole scenario as props.

The first solution should be used, as the provider should be the outermost component (so that everything underneath may connect properly).
You could either run a callback on componentDidReceiveProps or (what I would prefer) simply connect the component, which needs access to a store. This is exactly what redux is for. The only reason not to do this is if you would like to reuse a component with another stores content (truly representational component)
This strongly depends on the application and the depth of the components but in general, this is completely okay. You may also pass the information as props, but as your application grows bigger you may have to pass a lot of props, which may obfuscate the real intend of you components

Related

Implement Redux UI State Tree?

I am trying to make all React Components' data to be available through Redux. I need it so that any component can easily modify / interact with other component.
But I don't know how to inform Redux about the fact that React Component was mounted / unmounted, because the component itself has no info about its place in the React tree. Let's say that we have a list of three React Components in our main App.js:
<div>
<ClickCounter />
<ClickCounter />
<ClickCounter />
</div>
ClickCounter is implemented here:
import React from 'react'
function ClickCounter() {
const clicks = useSelector(state => state.UI.ClickCounter[/* What is my id?*/].clicks)
return (
<div>
</div>
)
}
export default ClickCounter
This lonely ClickCounter function does not know whether it was called by the first, second or third <ClickCounter />. No info about the React tree from the component level - no synchronizing with Redux.
I stuck and don't know how to implement it. Thanks for your time in advance!

Conditional rendering when using Redux (pure component and non-pure component)?

const LoginPage = ({ auth, doLogin, doLogout }) => (
<div>
<NavBar auth={auth}/>
<div className="row">
<div style={{display: 'flex', justifyContent: 'center'}} className="col-md-4 col-md-offset-4">
<LoginForm auth={auth} doLogin={doLogin} doLogout={doLogout}/>
</div>
</div>
</div>
);
// Connects state to the props of this component
const mapStateToProps = state => ({
auth: state.auth,
});
// Return a NEW component that is connected to the Store
export default connect(mapStateToProps, { doLogin, doLogout })(LoginPage);
Above is the code of the component called LoginPage.
It passes in the global state auth and two actions doLogin and doLogout. The LoginForm handles the user authentication, but I want to put another component called Dashboad and render it conditionally based on the auth's status.
However, I am not sure how to conditionally render in a pure component.
I could change into a non-pure component, but then I am not sure how to pass in auth, doLogin, and doLogout into a non-pure component.
Please help.
You can do almost everything you did in a non pure component in a pure component, except that PureComponent automatically does the shouldComponentUpdate by react itself to avoid unnecessary rerenders based on the shallow diffing of state and props.
If you want to conditionally render dashboard you can check the auth status from redux and use this.props.history.push("/dashboard") or <Redirect to="/dashboard"> to navigate to Dashboard component

Using Redux with react-grid-layout

I'm using react-grid-layout (RGL) to implement this use case with two considerations
to keep the state of the app including GridItems' child components
enable communication between GridItems' child components (events,
streams of data,
etc)
Inspecting the RGL tree on React Dev tools I can see that there are many intermediate components.
I was wondering if anyone has implemented a similar scenario, and if theres a way to use a Redux store and pass it down the RGL tree to GridItems' child components.
<ResponsiveReactGridLayout
<ReactGridLayout
<div
<GridItem
<DraggableCore
<Resizable
<div
<UserChildComponent
Many thanks,
FØ
Answering my own redux-noob question:
The fact that there are many invisible elements in the tree doesn't prevent passing the store down the element. Our custom-defined components (i.e., subcomponents of GridItem and leaves on the component tree) can get the store using the redux's connect as conventionally.
React-grid-layout
class Layout extends React.PureComponent {
render() {
return (
<div>
<ResponsiveReactGridLayout
onBreakpointChange={this.onBreakpointChange}
{...this.props}
>
{_.map(this.state.items, el => this.createElement(el))}
</ResponsiveReactGridLayout>
</div>
export default connect(state => state)(Layout);
The custom-defined component
class myCustomComponent extends Component {
export default connect(state => state) (myCustomComponent);
The resulting component tree in ReactDevTools,
<Connect(Layout)
<ResponsiveReactGridLayout
<ReactGridLayout
<div
<GridItem
<DraggableCore
<Resizable
<div
<Connect(UserChildComponent)
and the redux store and dispatch are part of their props.

Let AppBar component render title property of its child

I have this setup in my app
<App>
<DynamicView />
</App>
App should render the title property of its child DynamicView.
I've tried
<h1>{children[0].props.title}</h1>
But that property is empty because the the DynamicView is a connected Redux component. So the actual react tree looks like this:
<App>
<Connect(DynamicView) ...>
<DynamicView title="A Title"/>
</Connect(DynamicView)>
</App>
Does connect provide a way to access its connected components properties?
One way you can accomplish is by accessing the child's children:
<h1>{children[0].props.children[0].props.title}</h1>
However, this goes against the React philosophy of parent components being isolated from their children.
An alternative approach that is more in line with React/Redux principles is to put the title in the redux store (and it looks like you already did that, considering DynamicView gets it as a prop). You could make the App component a connected component that grabs the title from the store, and passes it as a prop into App.
class App extends React.Component {
render() {
return (
<div>
<h1>{this.props.title}</h1>
<DynamicView />
</div>
);
}
}
export default connect(
state => ({
title: state.title
})
)(App);

Differences passing Redux state through passProps with Navigator or binding state within Container

Testing React Native with Redux, and react-redux, I've found that the content of the state tree of Redux is not reflected in the display if the state is not passed through a Container.
To test this i use
react-native 0.19.0
react-redux 4.1.2
redux 3.1.7
as #alinzin explain in https://github.com/alinz/example-react-native-redux with all the tricks to setup this project. Are out there any other good solution?
The next example show a simple string as state in Redux store, and a simple action for enlarge the string.
Here is the reduce and the data as a single string in the state.
//Reducer
const reducer = (state = { data: '[][][]' }, action = {}) => {
switch (action.type) {
case ENLARGE:
return Object.assign({}, state, {
data: state.data+'[]'
})
default:
return state
}
}
The registred App is like this, nothing new. The initial route launches a Container for App1Component. This is the way for good binding state and Components (I belive)
export default class App extends Component {
render () {
return (
<Provider store={store}>
<Navigator style={{flex: 1}}
initialRoute={{
component: Container,
}}
renderScene={ (route, navigator) => {
const Component = route.component;
return (
<View style={{flex: 1, marginTop:40}}>
<Component navigator={navigator} route={route} {...route.passProps} />
</View>
);
}}
/>
</Provider>
)
}
}
The main content for both component App1Component and App2Componenet, is a simple component and just display the string, and show a 'button' to dispatch the enlarge action.
class MainContent extends Component{
render() {
return (
<View>
<Text>{this.props.data}-{this.props.data.length}</Text>
<TouchableOpacity onPress={()=>{this.props.enlarge()}} >
<Text>Click to Enlarge me!</Text>
</TouchableOpacity>
<Text> </Text>
</View>
)
}
}
There are no differences between components App1 and App2.
Just App1Component is invoked through a Container (and bind state and actions), and App2Component invoked (from App1Component) by pushing with Navigator all the same props (the same in App1Component)
Here is App1Component with a button to push next scene to App2Component and pass the same props, before binded through Container.
class App1Component extends Component {
render() {
return (
<View>
<MainContent {...this.props}/>
<TouchableOpacity onPress={()=>{
this.props.navigator.push({
name: 'App2',
component: App2Component,
passProps: {...this.props}
})
}}>
<Text>Click to Forward to App2Component {'\n'}passing props through Navigator passProps</Text>
</TouchableOpacity>
</View>
)
}
}
If you press 'enlarge' here the string is enlarged, and you can see the action reflected on the screen. Also you can see in chrome the trace for redux logger.
Here the wrapper container for App1Component
const Container = connect(
(state) => ({
data: state.data
}),
(dispatch) => ({
enlarge: () => dispatch(enlarge())
})
)(App1Component)
And Here Component 2, with back button.
If you press 'enlarge' here the string is enlarged, sure you can see the trace on chrome, but ... the action is not reflected on the screen.
class App2Component extends Component {
render() {
return (
<View>
<MainContent {...this.props}/>
<TouchableOpacity onPress={()=>{this.props.navigator.pop()}} >
<Text>Back to App1Component{'\n'}to see the change!</Text>
</TouchableOpacity>
</View>
)
}
}
After pressing Back, you can see the string modified on screen for App1Component.
As you can see I misunderstood something fundamental, but not what is.
Have i to decorate all the mains components (Pages/Scenes) with a container?
Why is wrong to pass props (previously binded by container) through passPros in Navigator? Actions are dispatched correctly, but does not reflect on the screen.
Any help is welcome.
Thanks
It looks like passProps does not propagate updates correctly. I’m not a React Native expert so I can’t confirm it, but I would definitely recommend you to use connect() for subscribing components to Redux store rather than passing props from the top.

Resources