I am creating a simple react application using material-ui. I am using MuiThemeProvider and ThemeProvider for theme.
App.js
import React from 'react';
import { createMuiTheme, MuiThemeProvider } from '#material-ui/core/styles';
import { ThemeProvider } from '#material-ui/styles';
import { CssBaseline } from '#material-ui/core';
import { Topic } from './dashboard/components/drawer/topic.js'
const theme = createMuiTheme({
palette : {
type : 'dark',
background : {
default : "#000000"
},
secondary : {
main : '#E19A4C'
}
}
})
function App() {
return (
< MuiThemeProvider theme={theme}>
<ThemeProvider theme={theme}>
<CssBaseline />
<div className="App">
<Dashboard />
<Topic />
</div>
</ThemeProvider>
</ MuiThemeProvider>
);
}
export default App;
Topic.js
import React, { Component } from 'react';
import { Typography, makeStyles, Box, Paper } from '#material-ui/core';
const style = makeStyles(theme => ({
paper : {
background : '#ff00ff'
}
}))
export const Topic = (props) => {
const classes = style()
return (
<div>
<Paper className={classes.box}>
<Typography variant="h6" component="h6" gutterBottom>
{props.data}
</Typography>
</Paper>
</div>
)
}
export default Topic
I am getting error Uncaught ReferenceError: theme is not defined
I have tried { withTheme : true } in makeStyles but it doesn't work.
Sending theme as props works but is not recommended.
Can Someone please help?
In your Topic.js file try using useTheme hook like this:
import React, { Component } from 'react';
import { Typography, makeStyles, Box, Paper } from '#material-ui/core';
import { useTheme } from '#material-ui/core/styles';
const style = makeStyles(theme => ({
paper : {
background : '#ff00ff'
}
}))
export const Topic = (props) => {
const classes = style()
const theme = useTheme();
return (
<div>
<Paper className={classes.box}>
<Typography variant="h6" component="h6" gutterBottom>
{props.data}
</Typography>
</Paper>
</div>
)
}
export default Topic
Now you can access the theme you created in App.js from theme variable(e.g const theme)
Related
I have created a navigation drawer which goes something like this,
const Minidrawer = props => {
const itemsList = [
{
text: "Home",
icon: <HomeIcon />,
onClick: () => history.push("/")
},
{
text: "My Cart",
icon: <UserIcon />,
onClick: () => history.push("/auth")
},
{
text: "Register Client",
icon: <GroupAddIcon />,
onClick:() => history.push("/register-client")
},
},
];
return (
<Drawer variant="permanent" open={open}>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{itemsList.map((item, index) => {
const { text, icon, onClick } = item;
return (
<ListItem button key={text} onClick={onClick}>
{icon && <ListItemIcon>{icon}</ListItemIcon>}
<ListItemText primary={text} />
</ListItem>
);
})}
</List>
</Drawer>
)}
export default withRouter(Minidrawer);
As you can see I am exporting this in wrapped withRouter because I'm calling browser router main index file
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { reducers } from './reducers';
import CssBaseline from '#mui/material/CssBaseline';
import { BrowserRouter as Router } from "react-router-dom";
import App from "./App";
const store = createStore(reducer, enhancer);
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<Router>
<CssBaseline />
<Provider store={store}><App />
</Provider>
</Router>
</React.StrictMode>,
rootElement
);
My app file where I am importing all the components
import React from "react";
import { Route, Switch } from "react-router-dom";
import Minidrawer from './components/Drawer/Minidrawer'
import { makeStyles } from '#mui/styles';
import Box from '#mui/material/Box';
import Main from "./components/Main/Main";
import {useSelector} from 'react-redux'
const useStyles = makeStyles({
container: {
display: "flex"
}
});
export default function App() {
const classes = useStyles();
const user = useSelector((state) => state.auth);
return (
<Box sx={{ display: 'flex' }}>
<Minidrawer currUser={user}/>
<Main/>
</Box>
);
}
This is my Main component file
import { styled, useTheme } from '#mui/material/styles';
import Box from '#mui/material/Box';
import Typography from '#mui/material/Typography';
import React from 'react';
import { Switch, Route, useHistory} from "react-router-dom";
import { Grid } from '#mui/material';
import Paper from '#mui/material/Paper';
import useStyles from './styles';
import RoutesMaster from "./RoutesMaster";
const RoutesRender = ({ Routes, Key}) => {
const History = useHistory();
console.log(Routes);
if (Routes.AuthRequired) {
History.push('/auth/login');
return null;
} else {
return <Route exact key={Key} path={Routes.Path} render={(props) => <Routes.Component {...props} />} />;
}
};
const Main = (props) => {
const classes = useStyles();
return (
<Box className={classes.box} sx={{ flexGrow: 1 }}>
<Switch>
{RoutesMaster.map((Routes, Index) => (
<RoutesRender Routes={Routes} Key={Index} />
))}
</Switch>
</Box>
)
}
export default Main;
Route Master file,
import Home from '../Home/Home';
import Auth from '../Auth/Auth';
import RegisterClient from '../RegisterClient/RegisterClient'
const RoutesMaster = [
{
Path: '/',
Component: Home,
Title: 'Home',
AuthRequired: false
},{
Path: '/auth',
Component: Auth,
Title: 'Auth',
AuthRequired: false
},{
Path: '/register-client',
Component: RegisterClient,
Title: 'Register Client',
AuthRequired: false
},
]
export default RoutesMaster;
The issue is ,on first load '/' I am getting rendered Home But when on clicking Button links in Mindrawer I can see above on browser URL is changing but the respective component is not getting rendered only getting blank in place of main component
Also one thing to note console.log(Routes); inside RoutesRender in Main.js fn always logs Home "/" path & blank UI
I am trying to create a background theme which will switch on onClick. On onClick it must change the background color of body in react app. I've managed to implement useContext, and now it toggles and changes the list items color in Header component. How to set it to body as well? Any help will be appreciated.
Here is my useContext color component
import React from 'react'
export const themes = {
light: {
foreground: '#ffffff',
},
blue: {
foreground: 'blue',
},
}
export default React.createContext({
theme: themes.light,
switchTheme: () => {},
})
onClick Button component
import React, { useContext } from 'react'
import ThemeContext from './context'
import './ThemedButton.scss'
const ThemedButton = () => {
const { switchTheme } = useContext(ThemeContext)
return (
<>
<button className="btn" onClick={switchTheme}>
Switch
</button>
</>
)
}
export default ThemedButton
App.js
import React, { useState } from 'react'
import SearchBar from './components/SearchBar';
import useCountries from './Hooks/useCountries';
import MainTable from './components/MainTable';
import ThemeButton from './useContext/ThemedButton';
import ThemeContext from './useContext/context';
import { searchProps } from './types';
import { themes } from './useContext/context';
import Routes from './Routes';
import './App.scss'
export default function App() {
const [search, setSearch] = useState('')
const [data] = useCountries(search)
const [context, setContext] = useState({
theme: themes.light,
switchTheme: () => {
setContext((current) => ({
...current,
theme: current.theme === themes.light ? themes.blue : themes.light,
}))
},
})
const handleChange: React.ReactEventHandler<HTMLInputElement> = (e): void => {
setSearch(e.currentTarget.value)
}
return (
<div className="App">
<SearchBar handleChange={handleChange} search={search as searchProps} />
<ThemeContext.Provider value={context}>
<ThemeButton />
<MainTable countries={data} />
</ThemeContext.Provider>
<Routes />
</div>
)
}
Header component
import React, { useContext } from 'react'
import ThemeContext from '../../useContext/context'
import './Header.scss'
export default function Header() {
const { theme } = useContext(ThemeContext)
return (
<div className="header">
<ul className="HeadtableRow" style={{ color: theme.foreground }}> // here it's set to change list items color
<li>Flag</li>
<li>Name</li>
<li>Language</li>
<li>Population</li>
<li>Region</li>
</ul>
</div>
)
}
If you want to change your body tag in your application you need to modify DOM and you can add this code to your Header.js (or any other file under your context) file:
useEffect(() => {
const body = document.getElementsByTagName("body");
body[0].style.backgroundColor = theme.foreground
},[])
*** Don't forget to import useEffect
*** Inline style like below is a better approach than modifying DOM directly
<div className="App" style={{backgroundColor: context.theme.foreground}}>
//For under context files just use theme.foreground
<SearchBar handleChange={handleChange} search={search as searchProps} />
<ThemeContext.Provider value={context}>
<ThemeButton />
<MainTable countries={data} />
</ThemeContext.Provider>
<Routes />
</div>
`
import React, { Component } from 'react';
import Header from './Components/UI/header';
import { Box, Paper } from '#material-ui/core';
import { ThemeProvider, withStyles} from '#material-ui/core/styles';
const styles = () => ({
overrides: {
'MuiPaper-root': {
root: {
backgroundColor: '#345f',
}
}
}
});
export class App extends Component {
render() {
const classes = this.props;
return(<ThemeProvider theme={theme}>
<Paper classes={classes.overrides}>
<Box display="flex" justifyContent="center" alignItems="center" height={1}>
<Header />
</Box>
</Paper>
</ThemeProvider>);
}
}
export default withStyles(styles)(App);
`project img link
Im trying to apply a different background color to the paper api component. When i run the code it generates the same base styles.
You don't need to worry about the MUI generated styles or root. This will set your local <Paper /> component's background
const styles = () => ({
overrides: {
backgroundColor: '#345f',
}
});
I have a connected component and have integrated MaterialUI in my component. For some reason, the tests keep on failing and I am not able to find some article online to resolve this.
Please advice.
Below is my code.
import React, {Component} from 'react';
import {connect} from 'react-redux';
import SourceCurrencyInput from './components/SourceCurrencyInput';
import TargetCurrencyInput from './components/TargetCurrencyInput';
import {fetchCurrencyConverterRates} from './actions/currencyConverterActions';
import CurrencyConverterValue from './components/CurrencyConverterValue';
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 {withStyles} from '#material-ui/core/styles';
import './App.css';
import {
changeSourceCurrencyValue,
changeTargetCurrencyValue
} from './actions/actions';
const styles = {
root: {
flexGrow: 1,
},
grow: {
flexGrow: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
};
class App extends Component {
componentDidMount() {
this.props.fetchCurrencyConverterRates(
this.props.srcCurrencyType,
this.props.tgtCurrencyType
);
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.srcCurrencyType !== this.props.srcCurrencyType
|| prevProps.tgtCurrencyType !== this.props.tgtCurrencyType) {
this.props.fetchCurrencyConverterRates(
this.props.srcCurrencyType,
this.props.tgtCurrencyType);
}
}
clearAll = () => {
this.props.sourceValue('');
this.props.targetValue('');
};
render() {
const {srcCurrencyType, tgtCurrencyType, srcCurrencyValue, tgtCurrencyValue, currencyConversionRate, classes} = this.props;
return (
<div className="App">
<AppBar position="static">
<Toolbar>
<Typography variant="h6" color="inherit" className={classes.grow}>
Currency Converter by Arun Manohar
</Typography>
</Toolbar>
</AppBar>
<header className="App-header">
<SourceCurrencyInput/>
<TargetCurrencyInput/>
<Button variant="contained" color="primary" className={classes.button}
onClick={() => this.clearAll()}>Clear</Button>
</header>
<CurrencyConverterValue srcCurrencyType={srcCurrencyType}
tgtCurrencyType={tgtCurrencyType}
srcCurrencyValue={srcCurrencyValue}
tgtCurrencyValue={tgtCurrencyValue}
currencyConversionRate={currencyConversionRate}
/>
<footer><a href={'https://currencyconverterapi.com'} target={'_blank'}>API from currencyconverterapi.com</a></footer>
</div>
);
}
}
const mapStateToProps = state => {
return {
srcCurrencyType: state.currencyConverterReducer.sourceCurrencyType,
tgtCurrencyType: state.currencyConverterReducer.targetCurrencyType,
srcCurrencyValue: state.currencyConverterReducer.sourceCurrencyValue,
tgtCurrencyValue: state.currencyConverterReducer.targetCurrencyValue,
currencyConversionRate: state.getConvertedRates.data[0]
};
};
const mapDispatchToProps = (dispatch) => ({
fetchCurrencyConverterRates: (src, tgt) => dispatch(fetchCurrencyConverterRates(src, tgt)),
sourceValue: (val) => dispatch(changeSourceCurrencyValue(val)),
targetValue: (val) => dispatch(changeTargetCurrencyValue(val)),
});
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(App));
Below is my test case.
import React from 'react';
import {Provider} from 'react-redux';
import configureStore from 'redux-mock-store';
import App from './App';
import {createMount} from '#material-ui/core/test-utils';
const mockStore = configureStore();
const initialState = {sourceCurrencyType: 'USD'};
const store = mockStore(initialState);
describe('<App />', () => {
let mount;
beforeEach(() => {
mount = createMount();
});
it('should work', () => {
const wrapper = mount(<Provider store={store}><App/></Provider>);
console.log(wrapper.debug());
});
});
This is the error I get.
TypeError: Cannot read property 'sourceCurrencyType' of undefined
I just need a way to access this component in my tests. Please help me out.
Your initial state must keep the same structure with the reducer object, such:
const initialState = {
currencyConverterReducer: {
sourceCurrencyType: 'USD',
// rest of attributes of currencyConverterReducer
}
};
Working with react-native and I get a problem with navigator.
Routes.js
import React from 'react';
import { createStackNavigator, createDrawerNavigator } from 'react-navigation';
import ItemListScreen from '../screens/ItemListScreen';
import ItemDetailsScreen from '../screens/ItemDetailsScreen';
export const RootStack = () => {
return createDrawerNavigator(
{
Home: {
screen: ItemList
},
ItemDetails: {
screen: ItemDetails
}
}
)}
export const ItemList = createStackNavigator({
ItemList: {
screen: ItemListScreen
}
},
{
headerMode: 'none'
});
export const ItemDetails = createStackNavigator({
ItemDetails: {
screen: ItemDetailsScreen
}
},
{
headerMode: 'none'
});
Header.js
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { Header, Body, Text, Icon, Left, Right } from 'native-base';
export default class AppHeader extends Component {
render() {
const headerText = this.props.headerText
return (
<Header>
<Left><Icon name='menu' onPress={()=> this.props.navigation.navigate('DrawerOpen')} /></Left>
<Body style={styles.header}>
<Text style={styles.headerText}>{headerText}</Text>
</Body>
<Right></Right>
</Header>
);
}
}
Index.js
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { Root, Button, Text, Drawer } from 'native-base';
import {RootStack} from './config/Routes';
import Header from './components/Header/Header';
import SideBar from './components/SideBar/SideBar';
export default class Index extends Component {
render() {
const Screen = RootStack();
const { globalContainer } = styles;
return (
<Root style={ globalContainer }>
<Header />
<Screen />
</Root>
)
}
}
The error is:
undefined is not an object (evaluating
'_this2.props.navigation.navigate')
The error is in OnPress() in Header.js
onPress={() => this.props.navigation.navigate('DrawerOpen')
What is the cause of this error? How to solve?
Your navigation object is not defined since you are not providing it the object.
You can include the navigation object using two ways,
Declare the object in the StackNavigator class
Pass navigation props explicitly. For example - in index.js you'll need to change <Header /> to <Header navigation={this.props.navigation} />. So, here you are providing it the necessary proprs in order to execute the navigate action.
EDIT
The actual issue is here,
<Root style={ globalContainer }>
<Header />
<Screen />
</Root>
you are defining your routes later, but calling your Header screen earlier. So precisely, navigation object is undefined in index.js itself.
What you should do is, list index.js in the StackNavigator class as the first object, so it'll be called first. So, your index.js will look something like this.
<Root style={ globalContainer }>
<Header navigation={this.props.navigation} /> //navigation object will be defined here
</Root>
Also, as i see, you have made your DrawerNavigator as your RootStack. I'll like to propose something different, You define a StackNavigator as your root stack, and then include drawer navigation in it.
Something on the lines of -
export const RootStack = createStackNavigator({
Index: //your index.js screen declaration
Drawer: //drawer navigator object
ItemDetails: {
screen: ItemDetailsScreen
}
},
EDIT 2
You'll be not be calling Rootstack in index.js. Your index.js will look something like this.
export default class Index extends Component {
render() {
const { globalContainer } = styles;
return (
<Root style={ globalContainer }>
<Header navigation={this.props.navigation}/>
</Root>
)
}
}
If index.js is your entry file, then you'll have to create a new entry file that calls the RootStack.
Something like entryFile.js
render() { return <RootStack /> }
which will automatically render all your routes and place index.js as your first screen.
Finally solved the issue. My approach has been given below:
Routes.js
import React from 'react';
import { createStackNavigator } from 'react-navigation';
import { Drawer } from './Drawer';
export const App = createStackNavigator(
{
Drawer: {
screen: Drawer
}
},
{
initialRouteName: "Drawer",
headerMode: "none"
}
)
Drawer.js
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { createDrawerNavigator } from 'react-navigation';
import ItemListScreen from '../screens/ItemListScreen';
import SideBar from '../components/SideBar/SideBar';
export const Drawer = createDrawerNavigator(
{
Home: { screen: ItemListScreen }
},
{
navigationOptions: {
gesturesEnabled: false
},
initialRouteName: "Home",
drawerPosition: 'left',
contentComponent: props => <SideBar {...props} />
}
);
Index.js
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { Button, Text, Drawer } from 'native-base';
import { App } from './config/Routes';
import AppHeader from './components/Header/Header';
export default class Index extends Component {
render() {
const { globalContainer } = styles;
return (
<App style={ globalContainer } navigation={this.props.navigation}></App>
)
}
}
Header.js
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { Header, Body, Text, Icon, Left, Right } from 'native-base';
export default class AppHeader extends Component {
render() {
const {navigation, headerText} = this.props
const {header, text, drawerIcon } = styles
return (
<Header>
<Left>
<Icon name='menu' style={drawerIcon} onPress={()=> navigation.openDrawer()} />
</Left>
<Body style={header}>
<Text style={text}>{headerText}</Text>
</Body>
<Right></Right>
</Header>
);
}
}