Cannot use connect function from react-redux - reactjs

I've been trying to use redux with the appbar component from the material-ui. But couldn't properly use the connect function.
Error message "Cannot call a class as a function" is being displayed in the console.
I'm using react 16.1.1 with redux 3.7.2 and react-redux 5.0.6
Let me know how to use the connect function in this context.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import {connect} from 'react-redux';
import * as dataTableAction from '../actions/dataTableAction';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';
const styles = theme => ({
root: {
width: '100%'
},
flex: {
flex: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
}
});
function ButtonAppBar(props) {
const { classes } = props;
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton className={classes.menuButton} color="contrast" aria-label="Menu">
<MenuIcon />
</IconButton>
<Typography type="title" color="inherit" className={classes.flex}>
Home
</Typography>
<Button color="inherit"
onClick={this.props.dispatch(dataTableAction.createDataTable(this.state.dataTable))}>
Change state
</Button>
</Toolbar>
</AppBar>
</div>
);
}
ButtonAppBar.propTypes = {
classes: PropTypes.object.isRequired
};
function mapStateToProps(state, ownProps) {
return{
dataTable: state.dataTable
}
}
export default withStyles(styles)(connect(mapStateToProps))(ButtonAppBar);

You need to inject the styles before calling connect
const styledComponent = withStyles(styles)(ButtonAppBar);
export default connect(mapStateToProps)(styledComponent);
you can also use the compose:
import compose from 'recompose/compose';
export default compose(
withStyles(styles),
connect(mapStateToProps, null)
)(ButtonAppBar);
Check out React Material UI - Export multiple higher order components for more info

Without Compose or recompose, you can use this below
export default withStyles(styles)(connect(mapStateToProps) (ButtonAppBar));

Related

Cannot find name 'classes'. TS2304?

I want to customize my appbar size on react Mui(currently using v4, but I get an error everytime I try to create a style.
My current Code
import React from "react";
import { AppBar, Toolbar, Typography, Box, makeStyles } from '#material-ui/core';
import { Link } from "react-router-dom";
import './Navbar.css'
const styles ={
customizeToolbar: {
minHeight: 36
}
};
function Navbar(){
return(
<AppBar position='sticky' >
<Toolbar variant='dense' style={classes.customizeToolbar} >
</Toolbar>
</AppBar>
)
}
export default Navbar;
import React from "react";
import { AppBar, Toolbar, Typography, Box, makeStyles } from '#material-ui/core';
import { Link } from "react-router-dom";
import './Navbar.css'
const styles ={
customizeToolbar: {
minHeight: 36
}
};
function Navbar(){
return(
<AppBar position='sticky' >
<Toolbar variant='dense' style={styles.customizeToolbar} >
</Toolbar>
</AppBar>
)
}
export default Navbar;
If you wish to customize the theme, you need to use the ThemeProvider component in order to inject a theme into your application. You must create styles with makeStyles, then you can create variable classes and use it in jsx with className not style. If you want customize not only Navbar but all components you can set ThemeProvider in index.js
import { createTheme, ThemeProvider } from '#mui/material/styles';
import { createStyles, makeStyles } from '#mui/styles';
const useStyles = makeStyles(() =>
createStyles({
customizeToolbar: {
minHeight: 36
}
})
);
const theme = createTheme();
function Navbar(){
const classes = useStyles();
return(
<ThemeProvider theme={theme}>
<AppBar position='sticky' >
<Toolbar variant='dense' className={classes.customizeToolbar} >
</Toolbar>
</AppBar>
</ThemeProvider>
)
}

How do I set the onClick in the navbar of material-ui?

Below is the code for creating the navigation bar/header for an application. I have used material-ui.
import React, { Component } from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import AccountCircle from "#material-ui/icons/AccountCircle";
import ShoppingCartOutlinedIcon from "#material-ui/icons/ShoppingCartOutlined";
const styles = (theme) => ({
root: {
width: "100%",
},
flex: {
flex: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
});
class Nav extends Component {
render() {
const { classes } = this.props;
return (
<AppBar position="static" elevation={0}>
<Toolbar>
<IconButton
className={classes.menuButton}
color="contrast"
onClick={this.props.toggleDrawer}
>
<MenuIcon />
</IconButton>
<Typography className={classes.flex} type="title" color="inherit">
Pizza Shop
</Typography>
<div>
<IconButton color="contrast" onClick={this.props.cart}>
<ShoppingCartOutlinedIcon />
</IconButton>
<IconButton color="contrast" onClick={this.props.login}>
<AccountCircle />
</IconButton>
</div>
</Toolbar>
</AppBar>
);
}
}
Nav.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Nav);
I'm new to 'props' and I'm not sure what this.props.cart or this.props.login will do. I want to create a functionality where when I click on the above two icons, I'm directed to some another component, but I'm not sure how to do it. Please help me to understand it.
Props are just parameters that the parent component sent to the children component. In your example this.props.cart and this.props.login are functions ( I am not sure about the cart, but it is used as a function ). From your example, when you click on the icons, you will call cart and login functions sent from the parent.
The answer to your question "How do you set onClick" is you already doing it on the login function/method. So you need to look at the parent component implementation.
Below I wrote a much more readable example, so feel free to look
import React from 'react'
class ChildrenComponent extends React.Component {
render() {
// This is doing the same thing, just we call the function / method in little different way
// return <div onClick={() => { this.props.onShoppingSubmit() }}>Aaa</div>
return <div onClick={this.props.onShoppingSubmit}>Aaa</div>
}
}
class ParentComponent extends React.Component {
handleShoppingSubmit = () => {
// Do what every that function needs to do
console.log('Hi from parent')
}
render() {
return (
<div>
<ChildrenComponent onShoppingSubmit={this.handleShoppingSubmit} />
</div>
)
}
}

Material-UI React TypeScript Component Styles Error w/ Default Theme

I'm integrating Material-UI (v4.4.3) into a React (v16.9.2) TypeScript (v3.6.3) website. Using the sample AppBar component example https://material-ui.com/components/app-bar/ and the TypeScript Guide https://material-ui.com/guides/typescript/#typescript I have the following functional component.
However, I'm getting a TS error for useStyles() on this line
const classes = useStyles();
(TS): Expected one argument, but got 0.
import * as React from 'react';
import { createStyles, Theme, makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Button from '#material-ui/core/Button';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
const useStyles = makeStyles(({ spacing }: Theme) =>
createStyles({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: spacing(2),
},
title: {
flexGrow: 1,
},
}),
);
export default function ButtonAppBar() {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
MSC
</Typography>
<Button color="inherit">Login</Button>
)}
</Toolbar>
</AppBar>
</div>
);
}
I want to use the default theme. Am I missing something?
Try to pass an empty object:
const classes = useStyles({});
try using components from :
import AppBar from "#material-ui/core/AppBar"
import XXX from "#material-ui/core/xxx"
XXX = other components
notes my dependencies are:
dependencies : "#material-ui/core": "^4.9.14"
devDependencies : "#types/material-ui": "^0.21.7"
don't forgot to import #types/material-ui devDependencies in your project.

Problem with showing a component which is in another file onclick

I am currently building an website in React with a navigation bar which I use Material-UI for.
My problem is that when I for example click "About" in my navigation bar, I want to show the content/component in About, and when I click Home I want the component Home to be shown and others hidden.
The problem is I am still a beginner in React and want to practice my React skills and now I have the navbar, Home, About in seperate files and not sure on how to pass through state, props and so in this case.
I will show a screen shot on the website and code-snippets to show my code so far.
My website:
File structure of my program:
Here is Code:
App.js:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import NavBar from './Components/Navigationbar'
import Home from './Components/Home'
import About from './Components/About'
class App extends Component {
constructor(props){
super(props);
this.state = {showAbout: true};
this.handleAbout = this.handleAbout.bind(this);
}
handleAbout(){
this.setState({showAbout: true})
}
render() {
return (
<div className="App">
<div className="App-header">
</div>
<NavBar></NavBar>
<p className="App-intro">
<Home></Home>
</p>
{this.state.showAbout ? <About /> : null}
</div>
);
}
}
export default App;
Home.jsx:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(3, 2),
backgroundColor: 'mistyrose'
},
}));
export default function PaperSheet() {
const classes = useStyles();
return (
<div>
<Paper className={classes.root}>
<Typography variant="h5" component="h3">
Home
</Typography>
<Typography component="p">
Welcome Home
</Typography>
</Paper>
</div>
);
}
About.jsx:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(3, 2),
backgroundColor: 'mistyrose'
},
}));
export default function PaperSheet() {
const classes = useStyles();
return (
<div>
<Paper className={classes.root}>
<Typography variant="h5" component="h3">
About
</Typography>
<Typography component="p">
About
</Typography>
</Paper>
</div>
);
}
And finally the navigation bar which is from Material UI:
Navigationbar.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import Button from '#material-ui/core/Button';
import App from '../App';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import About from './About';
const useStyles = makeStyles({
root: {
flexGrow: 1,
},
});
function handleAbout(props){
alert('About');
}
const navBar = (props) => {
return (
<Paper >
<Tabs
//value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
centered
>
<Tab label="Home" />
<Tab label="About" onClick={() => handleAbout(props)} />
<Tab label="Contact" />
</Tabs>
</Paper>
);
}
//ReactDOM.render(<navBar />, document.querySelector('#app'));
export default navBar;
My problem is I want to when I click "About" in the navbar, I want to show the About component(the content in About.jsx) on my website but not sure on how to handle state and props in the case when they are in seperate files.
Would appreciate if someone could help me.
Thanks a lot.
You can use react-router for navigation. How to install and use it is quite nicely shown on the page: https://reacttraining.com/react-router/web/guides/quick-start
Oh boy, this is a big one...
In the simplest case, you pass state though props like this:
<ChildComponent showAbout={this.state.showAbout}/>, and access it in ChildComponent by props.showAbout (or this.props.showAbout if it's a class component).
But things can get complicated as your application scales. Values can only be passed through props downwards inside the component tree; in other words, a component can only see a state that's somewhere above it. You can't use state from a sibling component or a component below it.
And that's the whole reason state management libraries exist. They provide a 'global' state that is available anywhere in the app. Redux is one of them.
You should sit down and learn Redux, as you can't really make a big app without a state management tool.
Another thing you should learn is react-router, for client-side routing.
Those things combined will provide a powerful tool for making useful apps.

Material-UI withStyles props in this.props.classes is undefined

I have two React components in a Create React App app, a parent component (App.js) and a child (QuoteMachine.js).
I applied the withStyles HOC API successfully in App.js, but when I used the same format to add one in QuoteMachine.js, passing this.props.classes.card as a property to the Material-UI Card component instance caused a TypeError Cannot read property 'props' of undefined.
Is there a problem with having the withStyles HOC in multiple components? Should I use a ThemeProvider instead?
QuoteMachine.js:
import React from 'react';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import Card from '#material-ui/core/Card';
import CardActions from '#material-ui/core/CardActions';
import CardContent from '#material-ui/core/CardContent';
import './QuoteMachine.css';
import { withStyles } from '#material-ui/core/styles';
const styles = {
card: {
padding: '30px'
}
};
// Parentheses around function body is implicit return
const QuoteMachine = (props) => (
<Card className={this.props.classes.card}>
<CardContent>
{props.isDoneFetching ?
(
<Typography>
<p className="quote">{props.randomQuote().quote}</p>
<p className="author">–{props.randomQuote().author}</p>
</Typography>
) : 'Loading...'}
</CardContent>
<CardActions>
<Button
size="large"
onClick={props.nextRandomQuote}
>
Next
</Button>
</CardActions>
</Card>
)
export default withStyles(styles)(QuoteMachine);
App.js:
import React, { Component } from 'react';
import QuoteMachine from './QuoteMachine';
import 'typeface-roboto'; // From Material-UI
import { Grid } from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import backgroundImage from './dawn.jpg';
const styles = {
// container is root component (set in Grid component instance)
container: {
display: 'flex',
alignItems: 'center',
height: '100vh',
background: `url(${backgroundImage}) center`,
backgroundSize: 'cover', // Using this in background causes issues
}
};
class App extends Component {
constructor(props) {
super(props);
...
}
....
render() {
return (
<Grid
id="quote-box"
className={this.props.classes.container}
justify="center"
container
>
<Grid xs={11} lg={8} item>
<QuoteMachine
isDoneFetching={this.state.isDoneFetching}
randomQuote={this.randomQuote}
nextRandomQuote={this.nextRandomQuote}
/>
</Grid>
</Grid>
);
}
}
export default withStyles(styles)(App);
Since QuoteMachine is a functional component, it doesn't have a this instance and the props are available from the argument to the functional component. You would access the classes like
<Card className={props.classes.card}>

Resources