I am trying to re-render the google map component when the state changes. I am using react-redux to change the state. The state changes successfully, but the map doesn't re-render. When I manually refresh the browser then only it takes new state value.
map.js
import React from 'react';
import { compose, withProps,withHandlers } from "recompose"
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps"
import { MarkerClusterer } from 'react-google-maps/lib/components/addons/MarkerClusterer';
import { connect } from 'react-redux';
const MyMapComponent = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyAoaDS6fIKlYvEHeTaakCxXqp-UwnggoEg",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withHandlers({
onMarkerClustererClick: () => (markerClusterer) => {
const clickedMarkers = markerClusterer.getMarkers()
console.log(`Current clicked markers length: ${clickedMarkers.length}`)
console.log(clickedMarkers)
},
}),
withScriptjs,
withGoogleMap
)((props) =>
<GoogleMap
defaultZoom={props.maps.zoom}
defaultCenter={{ lat:props.maps.lat, lng:props.maps.lng }}
>
<MarkerClusterer
onClick={props.onMarkerClustererClick}
averageCenter
enableRetinaIcons
gridSize={60}
>
{props.markers.map(marker => (
<Marker
key={marker.photo_id}
position={{ lat: marker.latitude, lng: marker.longitude }}
/>
))}
</MarkerClusterer>
</GoogleMap>
);
export default class DemoApp extends React.Component {
componentWillMount() {
this.setState({ markers: [] })
}
componentDidMount() {
console.log("+mymap++++++++");
console.log(this.props.myMap);
this.setState({markers:[{photo_id:1,longitude:76.911270,latitude:11.032595},
{photo_id:2,longitude:75.806682,latitude:11.259169},
{photo_id:3,longitude:77.213780,latitude:28.617671},
{photo_id:4,longitude:78.138991,latitude:9.903245}]})
}
render() {
return (
<MyMapComponent markers={this.state.markers} maps={this.props.myMap} />
)
}
}
In the above code I am giving dynamic values to the defaultcenter property of google map.
home.js
import React from 'react';
import ReactDom from 'react-dom';
import User from './users';
import * as ReactBootstrap from 'react-bootstrap';
import { connect } from 'react-redux';
import Card from './userdetails';
import Paper from 'material-ui/Paper';
import Menu from 'material-ui/Menu';
import MenuItem from 'material-ui/MenuItem';
import MapIcon from 'material-ui/svg-icons/communication/location-on';
import './homeStyle.css';
import DemoApp from './maps';
class Home extends React.Component {
viewProfile(e) {
console.log(e);
console.log("component in home");
this.props.userSelected(e);
}
render() {
const style = {
paper: {
display: 'inline-block',
float: 'left',
margin: '16px 32px 16px 0',
}
}
return (
<div style={{marginTop:68}}>
<h1>welcome </h1>
<ReactBootstrap.Col sm={12} >
<ReactBootstrap.Row>
<ReactBootstrap.Col sm={4}>
<ReactBootstrap.Col sm={12}>
<Paper style={style.paper}>
<Menu onItemClick={(event,menuitem,index)=>{console.log(menuitem.props.value);this.props.myMap();}} value="map menu">
<MenuItem value="maps" primaryText="My Location" leftIcon={<MapIcon />} />
</Menu>
</Paper>
</ReactBootstrap.Col>
<ReactBootstrap.Col sm={12}>
<DemoApp myMap={this.props.selectedMap} />
</ReactBootstrap.Col>
</ReactBootstrap.Col>
<ReactBootstrap.Col sm={4} style={{border:'1px solid black',paddingBottom:20,marginBottom:78,marginTop:10}}>
<User viewProfile={(e) => this.viewProfile(e)} />
</ReactBootstrap.Col>
{ this.props.selectedUser.id ?
<ReactBootstrap.Col sm={4} style={{border:'1px solid black',marginTop:10}}>
<Card details={this.props.selectedUser} />
</ReactBootstrap.Col>
: null
}
</ReactBootstrap.Row>
</ReactBootstrap.Col>
</div>
)
}
}
const mapStateProps=(state)=> {
console.log(state);
return {
selectedUser:state.userdetails,
selectedMap:state.mapReducer
}
}
const mapDispatchProps=(dispatch)=> {
return {
userSelected:(data)=> {
dispatch({type:"USER_SELECTED",data});
},
myMap:()=>{
dispatch({type:"MAPS"});
}
}
}
export default connect(mapStateProps,mapDispatchProps)(Home);
In the above code (home.js) <DemoApp /> is the map component and the state properties are passed through the props to it. When the user clicks on the menu item it dispatches an action called 'MAPS' and the state changes successfully, but the <DemoApp /> component doesn't re-render with its new values. What is the issue here?
Related
I am trying to access the object elements from appRoutes file via props. I made an appRoutes File from where I want to access all elements.
appRoutes File:
import HomePage from '../pages/home/HomePage';
import DashboardPageLayout from '../pages/dashboard/DashboardPageLayout'
import DashboardOutlinedIcon from '#mui/icons-material/DashboardOutlined';
import FormatListBulletedOutlinedIcon from '#mui/icons-material/FormatListBulletedOutlined';
import ChangeLog from "../pages/changlog/ChangeLog"
import DefaultPage from "../pages/dashboard/DefaultPage"
import SaasPage from "../pages/dashboard/SaasPage"
import AnalyticsPage from "../pages/dashboard/AnalyticsPage"
import DashboardIndex from '../pages/dashboard/DashboardIndex'
const appRoutes=[
{
index:true,
element:<HomePage/>,
state: "home"
},
{
path:"/dashboard",
element:<DashboardPageLayout/>,
state:"dashboard",
sidebarProps:{
displayText:"Dashboard",
icon: <DashboardOutlinedIcon />
},
child:[
{
index: true,
element:<DashboardIndex/>,
State:"dashboard.index",
},
{
path:"/dashboard/default",
element:<DefaultPage />,
state:"dashboard.default",
sidebarProps:{
displayText:"Default"
},
},
{
path: "/dashboard/analytics",
element:<AnalyticsPage/>,
state: "dashboard.analytics",
sidebarProps:{
displayText:"AnalyticsPage"
},
},
{
path:"/dashboard/saas",
element:<SaasPage/>,
state:"dashboard.saas",
sidebarProps:{
displayText:"SaasPage"
},
},
]
},
{
path: "/changelog",
element:<ChangeLog/>,
state:"changelog",
sidebarProps:{
displayText:"ChangeLog",
icon: <FormatListBulletedOutlinedIcon />
},
}
];
export default appRoutes;
I am trying to map all elements in my Sidebar file with route variable and passed in SidebarItem component with prop item.
sidebar.jsx:
import { Avatar, Drawer, List, Toolbar } from "#mui/material";
import { Stack } from "#mui/system";
import assets from "../../assets";
import colorConfigs from "../../configs/colorConfigs";
import appRoutes from "../../routes/appRoutes"
import sizeConfigs from "../../configs/sizeConfigs";
import SidebarItem from "./SidebarItem";
const Sidebar = () => {
return (<Drawer variant="permanent" sx={{
width: sizeConfigs.sidebar.width,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: sizeConfigs.sidebar.width,
boxSizing: "border-box",
borderRight: "0px",
backgroundColor: colorConfigs.sidebar.bg,
color: colorConfigs.sidebar.color
}
}}>
<List disablePadding>
<Toolbar sx={{ marginBottom: "20px" }}>
<Stack sx={{
width: "100%"
}} direction="row" justifyContent="center">
<Avatar src={assets.images.logo}/>
</Stack>
</Toolbar>
{appRoutes.map((route,index)=>(
route.sidebarProps ?(
<SidebarItem item = {route} key={index} />
) : null
)
)}
</List>
</Drawer>);
};
export default Sidebar;
in SidebarItem component I have defined item prop which is passed in sidebarItem.jsx.
SidebarItem.jsx:
import React from 'react'
import { ListItemButton,ListItemIcon } from '#mui/material'
import colorConfigs from '../../configs/colorConfigs'
const SidebarItem = ({item}) => {
return (
item.sidebarPropps && item.path ?
(
<ListItemButton>
<ListItemIcon sx={{color:colorConfigs.sidebar.color}}>
{item.sidebarProps.icon && item.sidebarProps.icon}
</ListItemIcon>
{item.sidebarProps.displayText}
</ListItemButton>
):null
)
}
export default SidebarItem
But this item prop is unable to access route elements from Sidebar.jsx file and does not displaying element from appRoutes.jsx file.
Please help to identify why my elements are not accessible from item props?
I am trying to avoid using Redux and want to share search results to components outside the header. My header appears on every page and the data is inside Lessons component. I want to search in the header and transfer the result to Lessons component and also to Students component. Any leads, please?
Below is my code.
app.js
import "./App.css";
import Lessons from "./components/Lessons";
import LoadingHOC from "./components/LoadingHOC";
import React, { useEffect, useState } from "react";
import { Route, BrowserRouter as Router, Switch, matchPath } from "react-router-dom";
import { ThemeProvider } from "#material-ui/core/styles";
import Header from "./components/Header";
import Footer from "./components/Footer";
import UpdateForm from "./components/UpdateForm";
import Register from "./components/Register";
import CreateLesson from "./components/CreateLesson";
import DeleteLesson from "./components/DeleteLesson";
import Students from "./components/Students";
import UpdateStudent from "./components/UpdateStudent"
import Login from "./components/Login";
import AddStudent from "./components/AddStudent";
import DeleteStudent from "./components/DeleteStudent";
import theme from "./theme";
function App() {
// const LessonsLoading = LoadingHOC(Lessons);
// // const StudentsLoading = LoadingHOC(Students);
/
// };
const [appState, setAppState] = useState({ loading: false, lessons: [] });
useEffect(() => {
console.log("debug app state");
setAppState({ loading: true });
// const apiUrl = "http://127.0.0.1:8000/api/";
// fetch(apiUrl)
// .then((data) => data.json())
// .then((lessons) => {
// debugger;
// setAppState({ loading: false, lessons: lessons });
// // console.log(lessons);
// });
}, []);
// console.log('debug loading', appState.loading)
// console.log('debug loading', appState.lessons)
return (
// console.log('debug lessons', appState.lessons),
// <div className="app">
// <LessonsLoading isLoading={appState.loading} lessons={appState.lessons}/>
// {/* <StudentsLoading isLoading={appState.loading} students={appState.students}/> */}
// </div>
<ThemeProvider theme={theme}>
{/* <Router> */}
<React.StrictMode>
<Header />
{/* <LoadingHOC /> */}
<Switch>
{console.log("debug lessons", appState.lessons)}
<Route
exact
path="/"
component={Lessons}
// lessons={appState.lessons}
/>
<Route exact path="/account/register" component={Register} />
<Route exact path="/account/login" component={Login} />
<Route path="/create/" component={CreateLesson} />
<Route path="/delete/" component={DeleteLesson} />
<Route path="/students" component={Students} />
<Route path="/student/:id" component={UpdateStudent}/>
<Route path="/lessons/:id" component={UpdateForm} />
<Route path="/add_student" component={AddStudent} />
{/* <Route exact path="/" component={App} /> */}
</Switch>
{/* if (!appState.loading) return <Lessons lessons={appState.lessons} />; */}
{/* <Footer /> */}
</React.StrictMode>
{/* </Router> */}
</ThemeProvider>
);
}
export default App;
header.js
import React, { useState, useEffect } from "react";
import {
fade,
makeStyles,
ThemeProvider,
createMuiTheme,
} 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 IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import AccountCircle from "#material-ui/icons/AccountCircle";
import Switch from "#material-ui/core/Switch";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import FormGroup from "#material-ui/core/FormGroup";
import MenuItem from "#material-ui/core/MenuItem";
import Menu from "#material-ui/core/Menu";
import InputBase from "#material-ui/core/InputBase";
import SearchIcon from "#material-ui/icons/Search";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
import { HistoryTwoTone } from "#material-ui/icons";
import axiosInstance from "./../axios";
import Students from './Students';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
mainTitle: {
textAlign: "center",
fontWeight: "700",
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
tableTitle: {
textAlign: "center",
},
search: {
position: "relative",
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
"&:hover": {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(1),
width: "auto",
},
},
searchIcon: {
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
inputRoot: {
color: "inherit",
},
inputInput: {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: "12ch",
"&:focus": {
width: "20ch",
},
},
},
}));
// Components lib - material, bootstrap, fabric, antd
// css-in-js: styled-components, theme-ui
export default function MenuAppBar() {
const classes = useStyles();
const [auth, setAuth] = React.useState(true);
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
// const [studentsData, setStudentsData] = useState("");
// useEffect(() => {
// axiosInstance.get("/students").then((res) => {
// setStudentsData({ ...studentsData, students: res.data });
// });
// }, []);
const handleChange = (event) => {
setAuth(event.target.checked);
};
const handleMenu = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div className={classes.root}>
<FormGroup>
<FormControlLabel
control={
<Switch
checked={auth}
onChange={handleChange}
aria-label="login switch"
/>
}
label={auth ? "Logout" : "Login"}
/>
</FormGroup>
<AppBar position="static" color="primary">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="menu"
onClick={handleMenu}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
Anat
</Typography>
{auth && (
<div>
<IconButton
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
// onClick={handleMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={open}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
{/* <Link href={"/students"} studentsList={studentsData.students}> */}
{/* <Link
to={{
pathname: "/students",
state: { studentsList: studentsData.students },
}}
> */}
<MenuItem component={Link} to={'/students'} onClick={handleClose}> Manage Students</MenuItem>
{/* </Link> */}
</Menu>
</div>
)}
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ "aria-label": "search" }}
/>
</div>
</Toolbar>
</AppBar>
{/* <div><Typography variant="h6" className="mainTitle">Student Lessons
</Typography>
</div> */}
{/* <div>
<h1 className={classes.tableTitle}>Student Lessons</h1>
</div> */}
</div>
);
}
lessons.js
import React, { useEffect, useState } from "react";
import { makeStyles,fade } from "#material-ui/core/styles";
import Table from "#material-ui/core/Table";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableHead from "#material-ui/core/TableHead";
import TableRow from "#material-ui/core/TableRow";
import Paper from "#material-ui/core/Paper";
import UpdateLesson from "./UpdateLesson";
import DeleteLesson from "./DeleteLesson";
import Container from "#material-ui/core/Container";
import Typography from "#material-ui/core/Typography";
import IconButton from "#material-ui/core/IconButton";
import EditIcon from "#material-ui/icons/Edit";
import Link from "#material-ui/core/Link";
import AddIcon from "#material-ui/icons/Add";
import Fab from '#material-ui/core/Fab';
import Grid from '#material-ui/core/Grid';
const useStyles = makeStyles((theme)=>({
table: {
minWidth: "450",
},
tableTitle: {
textAlign: "center",
},
title: {
textAlign: "center",
},
tablerow: {
fontWeight: "bold",
},
root: {
marginTop: theme.spacing(8),
paddingTop: theme.spacing(3),
paddingBottom: theme.spacing(3),
},
fab: {
backgroundColor: fade(theme.palette.primary.light),
// paddingHorizontal: auto,
}
}));
const Lessons = (props) => {
const [lessons, setLessons] = useState([]);
const apiUrl = "http://127.0.0.1:8000/api/";
useEffect(() => {
fetch(apiUrl)
.then((data) => data.json())
.then((lessons) => {
// debugger;
setLessons(lessons || []);
// console.log(lessons);
});
// setAppState({ loading: true });
}, []);
// const { lessons } = props;
console.log({ lessons });
const classes = useStyles();
// if (lessons === null) {
// return <div>No data</div>;
// }
return (
<React.Fragment>
<Container>
<div>
<h1 className={classes.tableTitle}>Student Lessons</h1>
</div>
<Paper>
<TableContainer component={Paper}>
<Table className={classes?.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell className={classes.tablerow}>Id</TableCell>
<TableCell align="right" className={classes.tablerow}>
Student
</TableCell>
<TableCell align="right" className={classes.tablerow}>
Title
</TableCell>
<TableCell align="right" className={classes.tablerow}>
lesson date
</TableCell>
<TableCell align="right" className={classes.tablerow}>
payment
</TableCell>
<TableCell
align="right"
className={classes.tablerow}
></TableCell>
<TableCell
align="right"
className={classes.tablerow}
></TableCell>
</TableRow>
</TableHead>
<TableBody>
{lessons ? (
lessons.map((lesson) => (
<TableRow key={lesson.id}>
<TableCell component="th">{lesson.id}</TableCell>
<TableCell align="right">{lesson.student}</TableCell>
<TableCell align="right">{lesson.title}</TableCell>
<TableCell align="right">{lesson.lesson_date}</TableCell>
<TableCell align="right">{lesson.paid}</TableCell>
<TableCell align="center">
{/* <UpdateLesson lesson={lesson.id} /> */}
<Link href={"/lessons/" + lesson.id}>
<IconButton className={classes.icon}>
<EditIcon />
</IconButton>
</Link>
</TableCell>
<TableCell align="right">
<DeleteLesson lessonId={lesson.id} />
</TableCell>
</TableRow>
))
) : (
<div>No data</div>
)}
</TableBody>
</Table>
</TableContainer>
</Paper>
</Container>
<Container maxWidth="md" className={classes.root}>
<Grid container spacing={2}>
<Link href={'/create'}>
<Fab className={classes.fab} >
<AddIcon />
</Fab>
</Link>
<Link href={'/students'}>
<Fab className={classes.fab} >
<AddIcon />
</Fab>
</Link>
</Grid>
</Container>
</React.Fragment>
);
};
export default Lessons;
you can store value inside variable...
app.js
import {useRef, useState, useEffect} from 'react'
import Header from './Header';
const app = () => {
const searchRef = useRef(null);
const [searchValue, setSearchValue] = useState(null);
useEffect(() => {
setSearchValue(searchRef.current.value);
})
return (
<Header ref={searchRef} />
)
}
header.js
import {forwardRef} from 'react'
const Header = ({other props like children etc.}, ref) => {
return (
<header>
<input ref={ref} type="text" placeholder="search" />
</header>
)}
const ForwardHeader = forwardRef(Header);
export default ForwardHeader;
From that point you can do what ever you want to do with searchValue variable inside app.js or forward it to some of the components. But better idia will be to use simple context to manage state of search input
import React from 'react';
import {GoogleMap, withScriptjs, withGoogleMap, Marker} from 'react-google-maps';
import {db} from './Firebase';
import {useState, useEffect} from 'react';
import InfoWindow from 'react-google-maps/lib/components/InfoWindow';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import { CssBaseline } from '#material-ui/core';
import { makeStyles } from '#material-ui/styles';
import IconButton from '#material-ui/core/IconButton'
import KeyboardArrowLeftRounded from '#material-ui/icons/KeyboardArrowLeftOutlined';
const useStyles = makeStyles( theme =>({
appBar: {
backgroundColor: '#1976d2'
},
card :{
marginTop: 60
}
}));
const Maps = (props) =>
{
const classes = useStyles();
const [positions, setPosition] = useState([])
useEffect(() => {
const unsub = db.collection('Location').onSnapshot (snapshot => {
const allPositions = snapshot.docs.map(doc => ({
id: doc.id,
... doc.data()
}));
setPosition(allPositions);
})
return () => {
unsub();
};
}, [])
const WrappedMap = withScriptjs(withGoogleMap(props => (
<GoogleMap defaultZoom = {13}
defaultCenter = {{ lat: -1.292066 , lng : 36.821945}}>
{
positions.map(positioning => (
props.isMarkerShown &&
<Marker key = {positioning.id}
position = {{lat: positioning.Latitude , lng: positioning.Longitude}}
></Marker>
)
)
}
{
positions.map(positioning => (
<InfoWindow key = {positioning.id} defaultPosition = {{lat: positioning.Latitude, lng: positioning.Longitude}}>
<div>
{positioning.Team} <br/>
Message
</div>
</InfoWindow>
))
}
</GoogleMap>)));
return (
<div>
<div>
<CssBaseline/>
<AppBar position = "fixed" color = "primary" className = {classes.appBar}>
<Toolbar>
<IconButton color = "inherit" edge = "start" onClick={() => props.history.goBack()}>
<KeyboardArrowLeftRounded/>
</IconButton>
</Toolbar>
</AppBar>
</div>
<div>
<Card className = {classes.card}>
<CardContent>
<div style = {{width: "97vw", height: "90vh"}}>
<WrappedMap
isMarkerShown
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=AIzaSyD29SDFXKcqARovEjwUqKl0ysEFKK7GCmU`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
</CardContent>
</Card>
</div>
</div>
)
};
export default Maps;
I am using the code above to render markers and infowindows for locations that I'm fetching from Firebase. Currently when an update is made in Firebase, the whole mapview is rendered again in order to update the position of the markers and infowindows. How do I go about re-rendering just the marker when an update is made in Firebase.
In your example WrappedMap gets re-created every time. One solution (to prevent Google Maps API reload and maps re-render) would be to place the instantiation of WrappedMap component outside of Maps:
const WrappedMap = withScriptjs(
withGoogleMap(props => (
<GoogleMap
defaultZoom={5}
defaultCenter={{ lat: -24.9929159, lng: 115.2297986 }}
>
{props.places.map(
position =>
props.isMarkerShown && (
<Marker
key={position.id}
position={{
lat: position.lat,
lng: position.lng
}}
></Marker>
)
)}
</GoogleMap>
))
);
and then just pass positions prop to reflect updated positions on map:
function Map() {
return (
<div>
<WrappedMap
isMarkerShown
positions={positions}
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=AIzaSyD29SDFXKcqARovEjwUqKl0ysEFKK7GCmU`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
You can prevent re-rendering by making use of PureComponent.
In your case positioning is re-rendering the Marker and InfoWindow.
The Updated Code below will only re-render the updated/newly-added Markers.
import React from 'react';
import {GoogleMap, withScriptjs, withGoogleMap, Marker} from 'react-google-maps';
import {db} from './Firebase';
import {useState, useEffect} from 'react';
import InfoWindow from 'react-google-maps/lib/components/InfoWindow';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import { CssBaseline } from '#material-ui/core';
import { makeStyles } from '#material-ui/styles';
import IconButton from '#material-ui/core/IconButton'
import KeyboardArrowLeftRounded from '#material-ui/icons/KeyboardArrowLeftOutlined';
const useStyles = makeStyles( theme =>({
appBar: {
backgroundColor: '#1976d2'
},
card :{
marginTop: 60
}
}));
class MarkerPureComponent extends React.PureComponent {
render () {
return (
<Marker
position = {this.props.position}
>
</Marker>
)
}
}
class InfoWindowPureComponent extends React.PureComponent {
render () {
return (
<InfoWindow defaultPosition = {this.props.defaultPosition}>
{this.props.children}
</InfoWindow>
)
}
}
const Maps = (props) =>
{
const classes = useStyles();
const [positions, setPosition] = useState([])
useEffect(() => {
const unsub = db.collection('Location').onSnapshot (snapshot => {
const allPositions = snapshot.docs.map(doc => ({
id: doc.id,
... doc.data()
}));
setPosition(allPositions);
})
return () => {
unsub();
};
}, [])
const WrappedMap = withScriptjs(withGoogleMap(props => (
<GoogleMap defaultZoom = {13}
defaultCenter = {{ lat: -1.292066 , lng : 36.821945}}>
{
positions.map(positioning => (
props.isMarkerShown &&
<MarkerPureComponent key = {positioning.id}
position = {{lat: positioning.Latitude , lng: positioning.Longitude}}
></MarkerPureComponent>
)
)
}
{
positions.map(positioning => (
<InfoWindowPureComponent key = {positioning.id} defaultPosition = {{lat: positioning.Latitude, lng: positioning.Longitude}}>
<div>
{positioning.Team} <br/>
Message
</div>
</InfoWindowPureComponent>
))
}
</GoogleMap>)));
return (
<div>
<div>
<CssBaseline/>
<AppBar position = "fixed" color = "primary" className = {classes.appBar}>
<Toolbar>
<IconButton color = "inherit" edge = "start" onClick={() => props.history.goBack()}>
<KeyboardArrowLeftRounded/>
</IconButton>
</Toolbar>
</AppBar>
</div>
<div>
<Card className = {classes.card}>
<CardContent>
<div style = {{width: "97vw", height: "90vh"}}>
<WrappedMap
isMarkerShown
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=AIzaSyD29SDFXKcqARovEjwUqKl0ysEFKK7GCmU`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
</CardContent>
</Card>
</div>
</div>
)
};
export default Maps;
I have a theme provider using the standard dark theme. I'd like to be able to access the details of this theme from my own custom component, but I cannot figure out how that would be done. In the example below, this.props.theme is undefined
ReactDOM.render(
<MuiThemeProvider theme={theme}>
<App/>
</MuiThemeProvider>, document.getElementById('root')
);
class App extends Component {
render() {
const {classes} = this.props;
return (
<div className="App">
<MainMenu/>
<div className={classes.root}>
<Grid container spacing={8}>
<Grid item xs>
<Chart theme={this.props.theme}/>
</Grid>
</Grid>
</div>
</div>
);
}
}
You can also use the useTheme hook!
import { useTheme } from '#material-ui/styles';
function DeepChild() {
const theme = useTheme();
return <span>{`spacing ${theme.spacing}`}</span>;
}
https://material-ui.com/styles/advanced/#accessing-the-theme-in-a-component
You need to use the HOC (Higher Order Component) provided with material-ui (note I am using the beta version, YMMV).
Example:
LeftNavigation.jsx
import React from 'react';
import PropTypes from 'prop-types';
import Hidden from 'material-ui/Hidden';
import Drawer from 'material-ui/Drawer';
import List from 'material-ui/List';
import Divider from 'material-ui/Divider';
import { withStyles } from 'material-ui/styles';
import { MailFolderListItems, OtherMailFolderListItems } from '../../../components/tileData';
const styles = theme => ({
docked: {
height: '100%',
},
drawerPaper: {
width: 250,
height: '100%',
[theme.breakpoints.up('md')]: {
width: theme.drawerWidth,
position: 'relative',
height: '100%',
},
},
drawerHeader: theme.mixins.toolbar,
});
class LeftNavigation extends React.Component {
render() {
const { classes, theme } = this.props;
const drawer = (
<div>
<div className={classes.drawerHeader} />
<Divider />
<List><MailFolderListItems toggle={this.props.handleDrawerToggle} /></List>
<Divider />
<List><OtherMailFolderListItems toggle={this.props.handleDrawerToggle} /></List>
</div>
);
return [
<Hidden mdUp key="mobile">
<Drawer
type="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={this.props.mobileOpen}
classes={{ paper: classes.drawerPaper }}
onRequestClose={this.props.handleDrawerToggle}
ModalProps={{ keepMounted: true /* Better open performance on mobile. */ }}
>
{drawer}
</Drawer>
</Hidden>,
<Hidden mdDown implementation="css" key="desktop">
<Drawer
type="permanent"
open
classes={{
docked: classes.docked,
paper: classes.drawerPaper,
}}
>
{drawer}
</Drawer>
</Hidden>,
];
}
}
LeftNavigation.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
mobileOpen: PropTypes.bool.isRequired,
handleDrawerToggle: PropTypes.func.isRequired
};
export default withStyles(styles, { withTheme: true })(LeftNavigation);
The const styles = theme => ({...}) is where you define your styles.
The export default withStyles(styles, { withTheme: true })(LeftNavigation); shows the usage with the higher order component to pass the styles / theme down to your component.
I use withTheme: true so that not only my styles, but the theme is passed as well.
So for your code, I would do the following:
import { withStyles } from 'material-ui/styles';
const styles = theme => ({
root: {
height: '100%'
}
})
class App extends Component {
render() {
const {classes} = this.props;
return (
<div className="App">
<MainMenu/>
<div className={classes.root}>
<Grid container spacing={8}>
<Grid item xs>
<Chart theme={this.props.theme}/>
</Grid>
</Grid>
</div>
</div>
);
}
}
export default withStyles(styles, { withTheme: true})(App);
And for people using Typesctipt and class components, you'll also need to add:
export interface MyComponentNameProps extends WithStyles<typeof styles, true> {
// your props here...
theme: Theme
}
Im new in React Material UI and I am trying to deploy AppBar but i do not know how can I include childs into this NavBar. I want deploy AppBar when I click on the three left lines menu. My .jsx is:
import React from 'react';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import FlatButton from 'material-ui/FlatButton';
const STYLES = {
title: {
cursor: 'pointer',
},
titleStyle: {
textAlign: 'center'
},
buttonStyle: {
backgroundColor: 'transparent',
color: 'white'
}
};
const rightButtons = (
<div>
<FlatButton label="About" style={STYLES.buttonStyle} />
<FlatButton label="Home" style={STYLES.buttonStyle} />
</div>
);
export default class MenuAlumno extends React.Component {
constructor() {
super();
this.state = {
abierto:false
}
}
handleTouchTap = () => {
//alert('Has clickado sobre el título');
/*
console.log(this.state.abierto)
this.setState({
abierto:true
});
*/
console.log(this.state.abierto)
this.state.abierto = true;
console.log(this.state.abierto)
}
render() {
return (
<AppBar
title={<span style={STYLES.title}>- PLATAFORMA DE INCIDENCIAS -
</span>}
onTitleTouchTap={this.handleTouchTap}
titleStyle={STYLES.titleStyle}
iconClassNameRight="muidocs-icon-navigation-expand-more"
iconElementLeft={rightButtons}
>
</AppBar>
);
}
}
But this code replaces the 3 left lines for FlatButtons.
I want that when I click on the MenuButtonLeft a side menu deploy with the pages that contain my website (home, about us, contact,...). With the code I put before only shows the MenuButtonLeft and a Title into a Toolbar, but it does not do any action, it does not deploy any menu with the href to my others pages in my website.
Thank you.
Use the following code.
import React from 'react';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import FlatButton from 'material-ui/FlatButton';
import Drawer from 'material-ui/Drawer';
const STYLES = {
title: {
cursor: 'pointer',
},
titleStyle: {
textAlign: 'center'
},
buttonStyle: {
backgroundColor: 'transparent',
color: 'white'
}
};
const rightButtons = (
<div>
<FlatButton label="About" style={STYLES.buttonStyle} />
<FlatButton label="Home" style={STYLES.buttonStyle} />
</div>
);
export default class MenuAlumno extends React.Component {
constructor() {
super();
this.state = {
abierto:false
}
}
handleTouchTap = () => {
//alert('Has clickado sobre el título');
/*
console.log(this.state.abierto)
this.setState({
abierto:true
});
*/
console.log(this.state.abierto)
this.state.abierto = true;
console.log(this.state.abierto)
}
render() {
return (
<div>
<AppBar
title={<span style={STYLES.title}>- PLATAFORMA DE INCIDENCIAS -
</span>}
onTitleTouchTap={this.handleTouchTap}
titleStyle={STYLES.titleStyle}
iconClassNameRight="muidocs-icon-navigation-expand-more"
>
</AppBar>
<Drawer docked={false} width={200} open={this.state.abierto} >
{rightButtons}
</Drawer>
</div>
);
}
}
I resolve the problem above! Here is the solution:
import React from 'react';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
import IconButton from 'material-ui/IconButton';
import NavigationMenu from 'material-ui/svg-icons/navigation/menu';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
/*
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import FlatButton from 'material-ui/FlatButton';
*/
const STYLES = {
title: {
cursor: 'pointer'
},
titleStyle: {
textAlign: 'center'
},
displayMenuTrue: {
position: 'relative'
},
displayMenuFalse: {
display: 'none'
},
contentStyle: {
transition: 'margin-left 450ms cubic-bezier(0.23, 1, 0.32, 1)',
marginLeft: '0px',
top: '0px'
},
contentStyleActive: {
marginLeft: '256px',
position: 'relative',
top: '-144px'
}
};
export default class MenuAlumno extends React.Component {
constructor() {
super();
this.state = {
drawerOpen:false
}
}
handleTouchTap = () => {
//alert('Has clickado sobre el título');
/*
console.log(this.state.abierto)
this.setState({
abierto:true
});
*/
console.log(this.state.drawerOpen)
this.state.drawerOpen = true;
console.log(this.state.drawerOpen)
}
controlMenu = () => {
if (this.state.drawerOpen) {
console.log(this.state.drawerOpen)
this.setState({
drawerOpen: false
});
$('.contenedor').css(STYLES.contentStyle);
} else {
console.log(this.state.drawerOpen)
this.setState({
drawerOpen: true
});
$('.contenedor').css(STYLES.contentStyle).css(STYLES.contentStyleActive);
}
}
render() {
return (
<div>
<AppBar
title={<span style={STYLES.title}>- PLATAFORMA DE
INCIDENCIAS -</span>}
onTitleTouchTap={this.handleTouchTap}
titleStyle={STYLES.titleStyle}
iconElementLeft={this.state.drawerOpen ? <IconButton>
<NavigationClose/></IconButton> : <IconButton><NavigationMenu/></IconButton>}
onLeftIconButtonTouchTap={this.controlMenu}
/>
<Drawer
open={this.state.drawerOpen}
containerStyle={this.state.drawerOpen ?
STYLES.displayMenuTrue : STYLES.displayMenuFalse}
>
<MenuItem>Menu Item</MenuItem>
<MenuItem>Menu Item</MenuItem>
<MenuItem>Menu Item</MenuItem>
</Drawer>
</div>
);
}
}