How can I pass params through react-navigation module without navigate to other page? - react-native

I am using react-navigation module to implement the bottom tab bar, and now I want to pass the parameters to page B during the operation in page A without page jumping, is there any way to achieve this?
I read the official documentation of react-navigation, and it seems that only the navigation.navigation method can pass parameters across pages, but this will cause page jumps

You can use the AsyncStorage package. Save the data you will use on PageB with a key on PageA. Then get saved data on the PageB.
import React, { useEffect } from 'react';
import { Button } from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
export default PageA = ({ navigation }) => {
useEffect(() => {
AsyncStorage.setItem("PageParams", "something")
}, [])
return (
<Button title='Navigate' onPress={() => navigation.navigate("PageB")} />
);
};
import { View } from 'react-native';
import React, { useEffect } from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
export default PageB = () => {
useEffect(async () => {
const params = await AsyncStorage.getItem("PageParams")
console.log(params) // output: something
})
return (
<View />
);
};

Related

How to fix an Objects are not valid as a React child Error?

I am very new to programming with React-native, and I was wondering if anyone could explain how I should fix this error? I was following along with a tutorial and had an error come up due to this section of code, even though it matched the tutorial code.
Here is the section of code:
import React, { createContext, useContext } from "react";
import * as Google from "expo-google-app-auth";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const signInWithGoogle = async() => {
await Google.logInAsync
}
return (
<AuthContext.Provider
value={{
user: null,
}}
>
{children}
</AuthContext.Provider>
);
};
export default function useAuth() {
return useContext(AuthContext);
}
These other two sections may be relevant as well:
Root of the App:
import React from 'react';
import { Text, View, SafeAreaView, Button, Alert } from 'react-native';
import AuthProvider from "./hooks/useAuth";
import StackNavigator from "./StackNavigator";
import { NavigationContainer} from "#react-navigation/native";
// Function for creating button
export default function App() {
return (
<NavigationContainer>
<AuthProvider>
<StackNavigator />
</AuthProvider>
</NavigationContainer>
);
}
This is my code for the Login Screen:
import React from 'react';
import { View, Text } from 'react-native';
import useAuth from '../hooks/useAuth';
const LoginScreen = () => {
const { user } = useAuth();
console.log(user);
return (
<View>
<Text>
Login to the app
</Text>
</View>
);
};
export default LoginScreen
This is the error that appears:
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
I suggest you auth with firebase. it makes easier this things.

Implement BackButton in React-Admin v4

Before version 4, we could create BackButton like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Button from '#material-ui/core/Button';
import ArrowBack from '#material-ui/icons/ArrowBack';
import { goBack } from 'react-router-redux';
class BackButton extends Component {
handleClick = () => {
this.props.goBack();
};
render() {
return <Button startIcon={<ArrowBack />} color="primary" onClick={this.handleClick}>Назад</Button>;
}
}
export default connect(null, {
goBack,
})(BackButton);
How do we create BackButton in react-admin version 4 ?
That library was deprecated a long time ago.
The natural upgrade of react-router-redux is connected-react-router.
In react-admin upgrade guide for version 4 is explained how to move away from connected-react-router:
Basically, now you have to use useNavigate hook from react-router:
https://marmelab.com/react-admin/Upgrade.html#removed-connected-react-router
Plus in your case you will have to change your component to a function component first.
Something like this:
import React, { Component } from 'react';
import Button from '#mui/material/Button';
import ArrowBack from '#mui/icons-material/ArrowBack';
import { useNavigate } from 'react-router';
const BackButton = props => {
const navigate = useNavigate();
handleClick = () => {
navigate(-1);
};
return (
<Button
startIcon={<ArrowBack />}
color="primary"
onClick={handleClick}
>
Назад
</Button>
);
}

Redux: mapStateToProps is not being called

I understand this kind of question was already asked several times here at StackOverflow. But I tried all the recommended solutions and nothing works for me. I'm running out of ideas.
The problem is with a React Native application for Android. Basically, the app provides a search bar to search an underlying database. The search results should be put into the store.
I use Redux v4.0.5, React-Redux v7.1.3, React v16.12.0 and React Native v0.61.5. For debugging, I use React Native Debugger in the latest version.
Now the simplified code. First, the component with the search bar. Here, mapStateToProps() is called. User makes an input and useEffect() immediately runs the database query, which should result in immediately calling mapStateToProps().
import React, {useEffect, useRef, useState} from 'react';
import {connect} from 'react-redux';
import {RootState} from '../../../rootReducer/rootReducer';
import {setResultValueSearchBar} from '../../../store/searchBar/actions';
imports ...
type Props = {};
const SearchBar: React.FC<Props> = () => {
const [returnValue, setReturnValue] = useState('');
const [inputValue, setInputValue] = useState('');
useEffect(() => {
// get query results
// logic to finally get a result string that should be put into the store
const resultNames: string = resultNamesArray.toString();
// method to set local and Redux state
const sendReturnValueToReduxStore = (resultNames: string) => {
setReturnValue(resultNames);
setResultValueSearchBar({resultValue: resultNames});
console.log('result value sent to store ', resultNames);
};
// call above method
sendReturnValueToReduxStore(resultNames);
}, [inputValue, returnValue]);
return (
<View>
<ScrollView>
<Header searchBar>
<Item>
<Input
placeholder="Search"
onChangeText={text => setInputValue(text)}
value={inputValue}
/>
</Item>
</Header>
</ScrollView>
</View>
);
};
function mapStateToProps(state: RootState) {
console.log("map state to props!", state); // is only called one time, initially
return {
resultValue: state.searchBarResult.resultValue,
};
}
const mapDispatchToProps = {
setResultValueSearchBar,
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);
Here is the rootReducer:
import {combineReducers} from 'redux';
import searchBarResultReducer from '../store/searchBar/reducers';
import reducer2 from '../store/reducer2example/reducers';
const rootReducer = combineReducers({
searchBarResult: searchBarResultReducer,
reducer2Result: reducer2,
});
export type RootState = ReturnType<typeof rootReducer>;
Here is the searchBarResultReducer in reducers.ts file:
import {
SearchBarResultState,
SET_RESULT_VALUE_SEARCHBAR,
ResultValueType,
} from './types';
const initialState: SearchBarResultState = {
resultValue: 'No results',
};
// take state and action and then return a new state
function searchBarResultReducer(
state = initialState,
action: ResultValueType,
): SearchBarResultState {
console.log('invoked result: ', action.type); // called only initially
if (action.type === 'SET_RESULT_VALUE_SEARCHBAR') {
return {
...state,
...action.payload,
};
} else {
return state;
}
}
export default searchBarResultReducer;
And the corresponding types.ts ...
export const SET_RESULT_VALUE_SEARCHBAR = 'SET_RESULT_VALUE_SEARCHBAR';
export interface SearchBarResultState {
resultValue: string;
}
interface ResultValueAction {
type: typeof SET_RESULT_VALUE_SEARCHBAR;
payload: SearchBarResultState;
}
export type ResultValueType = ResultValueAction
... and the actions.ts:
import {SET_RESULT_VALUE_SEARCHBAR, ResultValueType, SearchBarResultState} from './types'
export const setResultValueSearchBar = (resultValue: SearchBarResultState): ResultValueType => ({
type: SET_RESULT_VALUE_SEARCHBAR,
payload: resultValue,
});
And index.js:
import React from 'react';
import {AppRegistry} from 'react-native';
import {createStore, applyMiddleware, compose} from 'redux';
import {Provider} from 'react-redux';
import App from './App';
import {name as appName} from './app.json';
import rootReducer from './src/rootReducer/rootReducer';
import Realm from 'realm';
import { composeWithDevTools } from 'redux-devtools-extension';
import invariant from 'redux-immutable-state-invariant';
const composeEnhancers = composeWithDevTools({});
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(invariant()))
);
const Root = () => {
Realm.copyBundledRealmFiles();
return (
<Provider store={store}>
<App />
</Provider>
);
};
AppRegistry.registerComponent(appName, () => Root);
To summarize: Whenever the database query succeeds, the result value should be sent to the store. But in the React Native Debugger/Redux Devtools, the reducer/mapStateToProps() is called only once and only, as shown by the console.log s in the code.
What is going on here?
Solved! As stated by Hemant in this Thread, you also have to pass the action that you import as props into the component. Works like a charm now :)

Passing props to a component using StackNavigator in React Native

import React, {Component} from 'react'
import { StackNavigator } from 'react-navigation'
import {connect} from 'react-redux'
import {getAllUsers} from '../actions'
import {List, ListItem} from 'react-native-elements'
import { StyleSheet, Text, View, FlatList } from 'react-native'
const UserDetail = () => {
<View>
<Text>User Detail</Text>
</View>
}
const Home = ({ navigation }) => (
<List>
{typeof users === 'string' &&
<FlatList
data={users}
/>
}
</List>
)
const Stack = StackNavigator({
Home: {
screen: Home
},
UserDetail: {
screen: UserDetail
}
})
class MainScreen extends Component {
componentDidMount(){
this.setState({ users : this.props.getAllUsers() })
}
render() {
const users = typeof this.props.decks === 'string'
? Object.values(JSON.parse(this.props.users)) : ''
return(
<Stack />
)
}
}
function mapStateToProps(users) {
return {
users: users,
}
}
function mapDispatchToProps(dispatch) {
return {
getAllUsers: () => dispatch(getAllUsers()),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(MainScreen)
I have two questions:
1) I want to pass the users props from MainScreen component to Home component and later on also to UserDetail component. But I could not see any example on that on React Navigation documentation except for ScreenProps.
2) If I go to UserDetail and let's say it would be a separate component. And let's say it would call another component. How could it go back to Home for example?
I'm using Redux by the way also here and there are some who suggest to do it in Redux but still I could not make it work also. I am fairly new to React Native or React for that matter, but I could not find a definitive answer so far on this above questions.

Navigation in React Router v4 Native Not Changing Scenes

I'm using React Router for a React Native app. Not sure what I'm missing here but I install react-router-native and require the history package, and setup a couple methods that just push a new route onto the stack but nothing happens. I console.log('clicked'); to check that it's firing and it is so not sure what's wrong.
import React, { Component } from 'react';
import { View } from 'react-native';
import Splash from './Splash';
import createHistory from 'history/createMemoryHistory';
const history = createHistory();
class SplashContainer extends Component {
goToLogin = () => {
history.push('/Login');
}
goToRegister = () => {
history.push('/SignUp');
}
render () {
console.log(history)
return (
<Splash
goToLogin={this.goToLogin}
goToRegister={this.goToRegister}
/>
);
}
}
export default SplashContainer;
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { Button } from 'native-base';
import { Link } from 'react-router-native';
import PropTypes from 'prop-types';
const Splash = (props) => {
console.log(props)
return (
<View style={styles.container}>
<Button light block onPress={props.goToLogin}>
<Text>Login</Text>
</Button>
<Button dark block bordered style={{marginTop: 10}} onPress={props.goToRegister}>
<Text>Register</Text>
</Button>
</View>
);
}
Splash.propTypes = {
goToLogin: PropTypes.func.isRequired,
goToRegister: PropTypes.func.isRequired
}
export default Splash;
I don't know your Router config, but your methods should be:
goToLogin = () => {
const { history } = this.props
history.push('/Login');
}
history will passed down via props of component inside Router's stack.