Implement BackButton in React-Admin v4 - react-admin

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>
);
}

Related

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

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 />
);
};

undefined is not a function (evaluating 'navigation.dispatch')

I am trying to navigate to the first page after user logout!
and getting this error:
undefined is not a function (evaluating 'navigation.dispatch').
my action creator code is as follows:
import {NavigationActions} from 'react-navigation';
import {AsyncStorage} from 'react-native';
import {applyMiddleware as dispatch} from "redux";
export const userRemove = (navigation) => {
AsyncStorage.removeItem('user');
const navigateAction = NavigationActions.navigate({
routeName: 'First',
props: {}
});
navigation.dispatch(navigateAction);
};
I have called this action from sideBar of component. The component code is correct since that AsyncStorage.removeItem('user') is working correctly!
sorry for bad grammar. I can't write in english better than this error message here
my component code here:
import React, {Component} from 'react';
import {
Container,
Content,
Footer,
FooterTab,
Button,
Icon
} from 'native-base';
import {userRemove} from '../actions/sideBarAction';
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
class SideBar extends Component {
constructor(props) {
super(props);
}
logout() {
userRemove(this.props.navigation);
}
render() {
return (
<Container>
<Content>
...
</Content>
<Footer>
<FooterTab>
<Button onPress={this.logout.bind(this)}>
<Icon name="logout"/>
</Button>
</FooterTab>
</Footer>
</Container>
)
}
}
const mapStateToProps = state => {
return {
username: state.home.username,
password: state.home.password,
score: state.home.score,
}
};
const mapDispatchToProps = dispatch => {
return Object.assign({dispatch: dispatch},
bindActionCreators(userRemove, dispatch));
};
connect(mapStateToProps, mapDispatchToProps)(SideBar);
Is your component added to you main navigator? If not see if you can add.
You can also wrap your component with withNavigation function from react-navigation. That will make sure you have valid navigation prop.
import { withNavigation } from react-navigation;
...
connect(mapStateToProps, mapDispatchToProps)(withNavigation(SideBar));
Alternatively, you may pass this.props.navigation from parent component too.

Error: Check the render method with react-redux connect()

I'm trying to learn redux with react-native, so I did some examples, but for everyone, even though they are different, I'm getting the same error.
My code is like this:
index.js:
import { AppRegistry } from 'react-native';
import App from './src/app';
AppRegistry.registerComponent('reduxExample', () => App);
src/app.js:
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { Store } from './store';
import Home from './components/home';
export default props => (
<Provider store={Store}>
<Home />
</Provider>
)
src/components/home.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text, Button, Input } from 'react-native';
class Home extends Component {
render() {
const { newValue } = this.props;
return (
<View>
<Input type='text' />
<Button>
Click me!
</Button>
<Text>{newValue}</Text>
</View>
);
}
}
const mapStateToProps = store => ({
newValue: store.clickState.newValue
});
export default connect(mapStateToProps)(Home)
src/store/index.js:
import { createStore } from 'redux';
import { Reducers } from '../reducers';
export const Store = createStore(Reducers);
The rest I believe they do not have to deal with the problem but if I need to put the code.
There is no <Input type='text' /> tag in react native. There is only a <TextInput /> tag.

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.

Passing in action creators to react-navigation

I'm using the new react-navigation library for a React Native application I'm building. I'm having an issue with passing down my ActionCreators from my Nav component down to its scenes.
I have an AppContainer that wraps the entire application.
import React, { Component } from 'react';
import { DrawerNavigator, addNavigationHelpers } from 'react-navigation';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { ActionCreators } from '../actions';
import DashboardContainer from './DashboardContainer';
import CustomersContainer from './CustomersContainer';
const ApplicationNavigation = DrawerNavigator({
Dashboard: { screen: DashboardContainer },
Customers: { screen: CustomersContainer },
});
class AppContainer extends Component {
render() {
return (
<ApplicationNavigation />
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect(() => { return {} }, mapDispatchToProps)(AppContainer);
Here is the CustomerContainer:
import React, {Component} from 'react';
import {View, Text, Button} from 'react-native';
export default class CustomerContainer extends Component {
btnPressed() {
this.props.listCustomers()
}
render () {
return (
<View style={{marginTop: 40}}><Text>Customer</Text>
<Button onPress={() => this.btnPressed()} title="Press Me!" />
</View>
);
}
}
Now I'm trying to call an action within my CustomerContainer this.props.listCustomers(). The problem is the ActionCreator props aren't being passed down to the screens. I've tried doing adding the screenProps prop to the ApplicationNavigation component:
But for some reason when I do this my app doesn't display any screens its just blank with no errors.
UPDATE
So I updated my CustomerContainer file:
import React, {Component} from 'react';
import {View, Text, Button} from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { ActionCreators } from '../actions';
class CustomerContainer extends Component {
btnPressed() {
console.log(this.props.listCompanyCustomers())
}
render () {
return (
<View style={{marginTop: 40}}><Text>Customer</Text>
<Button onPress={() => this.btnPressed()} title="Press Me!" />
</View>
);
}
}
function mapStateToProps(state) {
return {
companyCustomers: state.companyCustomers
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(CustomerContainer);
This now works; however this feels like the incorrect way to go about this.
What redux connect basically does is:
Wrap your component
Declare contextProps to get access to dispatch
pass the dispatch to your component props.
If you pass the connect mapDispatchToProps, then it creates the mapping of the methods to dispatch.
So if you connect you AppContainer, its children won't get these dispatch methods, unless AppContainer passes them to its children (but this what connect comes to prevent).
So to sum up, you should connect any component that needs to use dispatch, otherwise it won't get it.
If you don't want to copy paste the mapDispatchToProps, you can just delete it and use this.props.dispatch instead:
import { ActionCreators } from '../actions';
class CustomerContainer extends Component {
btnPressed() {
this.props.dispatch(ActionCreators.listCompanyCustomers());
}
render () {
return (
<View style={{marginTop: 40}}><Text>Customer</Text>
<Button onPress={() => this.btnPressed()} title="Press Me!" />
</View>
);
}
}