React: ProgressiveMobileStepper handleNext function does not work (Material UI) - javascript

Dear community I am currently writing a website and its user onboarding using the ProrgressiveMobileStepper using Material UI. You can find my website here:
https://konekto-eeioq6hwh.now.sh/first_use
Apart from the formatting being screwed up (for which I would also appreciate your help), I am mainly concerned that the next button does not work. I have structured that the several screens are conditionally rendered in index.js depending on the state and the handleNext function should be called from ProgressiveMobileStepper when the button is clicked. However, the screen does not update then.
index.js file:
import React from 'react';
import { Grid, Container } from '#material-ui/core';
import { Header } from '../Layout';
import logo from './android-icon-192x192.png';
import { withStyles } from '#material-ui/core/styles';
import ExplanationAboutProcess0 from './Explanation0';
import ExplanationAboutProcess1 from './ExplanationProcess1';
import ExplanationAboutProcess2 from './ExplanationProcess2';
import ExplanationAboutProcess3 from './ExplanationProcess3';
import ProgressiveMobileStepper from './ProgressiveMobileStepper';
const styles = theme => ({
container: {
alignItems: 'center',
border: 'black',
'border-width': 'medium',
'margin-top': '80px',
background: 'rgba(255, 255, 255, 0.8)',
'border-radius': '20px'
},
picture: { display: 'block', margin: '0 auto' },
box: { width: '230px' }
});
class FirstUse extends React.Component {
constructor(props) {
super(props);
this.classes = props.classes;
this.state = {
componentType: 'explanationaboutprocess0'
};
this.handleNext = this.handleNext.bind(this);
this.handleBack = this.handleBack.bind(this);
}
fun() {
//this.props.history.push('/settings');
}
handleNext(e) {
console.log(e);
console.log(this.componentType);
if (this.componentType == 'explanationaboutprocess0')
this.setState({ componentType: 'explanationaboutprocess1' });
}
handleBack(e) {
console.log(e);
if (this.componentType == 'explanationaboutprocess1')
this.setState({ componentType: 'explanationaboutprocess0' });
}
render() {
let component;
if (this.state.componentType == 'explanationaboutprocess0') {
component = (
<ExplanationAboutProcess0
handleNext={this.handleNext}
handleBack={this.handleBack}
/>
);
} else if (this.state.componentType == 'explanationaboutprocess1') {
component = (
<ExplanationAboutProcess1
handleComponentType={this.handleComponentType}
handleBack={this.handleBack}
/>
);
} else if (this.state.componentType == 'explanationaboutprocess2') {
component = (
<ExplanationAboutProcess2
handleComponentType={this.handleComponentType}
handleBack={this.handleBack}
/>
);
} else if (this.state.componentType == 'explanationaboutprocess3') {
component = (
<ExplanationAboutProcess3
handleComponentType={this.handleComponentType}
handleBack={this.handleBack}
/>
);
}
return (
<React.Fragment>
<Header title="Learn how to send SOS" />
<Grid
container
className={this.classes.container}
direction="column"
spacing={2}
>
<Grid item sm={12} className={this.classes.item}>
<img src={logo} alt="Logo" />
</Grid>
<Grid item sm={12} className={this.classes.item} />
<Container component="main" maxWidth="sm">
{component}
</Container>
</Grid>
<Grid item sm={12} className={this.classes.item}>
<ProgressiveMobileStepper />
</Grid>
</React.Fragment>
);
}
}
export default withStyles(styles)(FirstUse);
ProgressiveMobileStepper.js file:
import React from 'react';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import MobileStepper from '#material-ui/core/MobileStepper';
import Button from '#material-ui/core/Button';
import KeyboardArrowLeft from '#material-ui/icons/KeyboardArrowLeft';
import KeyboardArrowRight from '#material-ui/icons/KeyboardArrowRight';
const useStyles = makeStyles({
root: {
maxWidth: 400,
flexGrow: 1
}
});
export default function ProgressMobileStepper(props) {
const classes = useStyles();
const theme = useTheme();
const [activeStep, setActiveStep] = React.useState(0);
return (
<MobileStepper
variant="progress"
steps={6}
position="static"
className={classes.root}
nextButton={
<Button
size="small"
onClick={props.handleNext}
disabled={activeStep === 5}
>
Next
{theme.direction === 'rtl' ? (
<KeyboardArrowLeft />
) : (
<KeyboardArrowRight />
)}
</Button>
}
backButton={
<Button
size="small"
onClick={props.handleBack}
disabled={activeStep === 0}
>
{theme.direction === 'rtl' ? (
<KeyboardArrowRight />
) : (
<KeyboardArrowLeft />
)}
Back }
</Button>
}
/>
);
}
Just as an example, you can see two of the screens, which should be conditionally rendered below:
Explanation0.js file:
import React from 'react';
import { Typography } from '#material-ui/core';
export default class ExplanationAboutProcess1 extends React.Component {
constructor(props) {
super(props);
this.classes = props.classes;
this.state = {};
}
render() {
return (
<React.Fragment>
<Typography>
Konnekto allows you to send an emergency request from you phone
without requiring a celluar connection. Please enter your personal
information.
</Typography>
</React.Fragment>
);
}
}
ExplanationProcess1.js file:
import React from 'react';
export default class ExplanationAboutProcess1 extends React.Component {
constructor(props) {
super(props);
this.classes = props.classes;
this.state = {};
}
render() {
return (
<React.Fragment>
<p>
To explain you how you would use the app in case of emergency, we
guide you through which questions we ask you:
<br />
0.1 Are you affected yourself?
<br />
0.2 How do you want to contact?
</p>
</React.Fragment>
);
}
}
I would really appreciate your help!

Related

1 All instances of a component reference the first instance instead of itself; 2 Children props get updated only after calling parent's setState twice

UPDATE: I easily fixed the first issue by providing unique names for all the 'Rating' components, but the question about 2 x setState fixing props remains open.
I actually have two questions. The second one emerged from trying to solve the first one.
The initial problem was that 'this' in each of the onChange Rating's property in each of the SongScoring components pointed to the first instance of the SongScoring class instead of itself:
Parent code:
import React, { Component } from 'react';
import './Assess.css';
import { Accordion } from 'react-bootstrap';
import SongScoring from './SongScoring';
class Assess extends Component {
constructor(props) {
super(props);
const songs = [
{
track: "song1",
},
{
track: "song2",
},
{
track: "song3",
},
{
track: "song4",
},
]
this.state = {
songs: songs,
};
}
render() {
return (
<div className="root">
<Accordion defaultActiveKey="0">
{
this.state.songs.map((song, index) => (
<SongScoring song={song} key={index.toString()} index={index} />
))
}
</Accordion>
</div>
);
}
}
export default Assess;
Child code:
import React, { Component } from 'react';
import './SongScoring.css';
import { Accordion, Card, Container, Row } from 'react-bootstrap';
import ReactPlayer from "react-player";
import Rating from '#material-ui/lab/Rating';
import Box from '#material-ui/core/Box';
const labels = {
0.5: 'Unpleasant',
1: 'Bearable',
1.5: 'Bearable+',
2: 'Intriguing',
2.5: 'Intriguing+',
3: 'Ok',
3.5: 'Ok+',
4: 'Pleasant',
4.5: 'Pleasant+',
5: 'Excellent',
};
class SongScoring extends Component {
constructor(props) {
super(props);
this.state = {
song: props.song,
key: props.index.toString(),
score: props.song.score || 0,
hover: props.song.score || -1,
onChange: this.props.onChange,
onChangeActive: this.props.onChangeActive
}
}
render() {
return (
<>
<Card>
<Accordion.Toggle as={Card.Header} variant="link" eventKey={this.state.key}>
{this.state.key}
<Rating name="read-only" value={this.state.score} precision={0.5} readOnly />
</Accordion.Toggle>
<Accordion.Collapse eventKey={this.state.key}>
<Card.Body style={{ display: 'flex', alignItems: 'center', lineHeight: '1' }}>
<Container>
<Row className='scoring-row'>
<ReactPlayer
url="https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3"
width="400px"
height="50px"
playing={false}
controls={true}
style={{ outline: 'none' }}
/>
</Row>
<Row className='scoring-row'>
<Rating
name="song-rating"
value={this.state.score}
precision={0.5}
onChange={(event, newScore) => {
this.setState({
score: newScore
});
}}
onChangeActive={(event, newHover) => {
this.setState({
hover: newHover
});
}}
style={{ marginTop: '40px' }}
/>
</Row>
<Row className='scoring-row'>
{this.state.score !== 0 && <Box>{labels[this.state.hover !== -1 ? this.state.hover : this.state.score]}</Box>}
</Row>
</Container>
</Card.Body>
</Accordion.Collapse>
</Card>
</>
);
}
}
export default SongScoring;
After multiple iterations of trying to solve this problem by changing from arrow functions and binding, putting onChange and onChangeActive into the state etc. I finally decided to try to solve this problem by passing the onChange and onChangeActive functions from the parent to the child.
This is what I came up with (there is console.log instead of setState in onChange and onChangeActive, but what we care about is 'this' reference really):
Parent code:
import React, { Component } from 'react';
import './Assess.css';
import { Accordion } from 'react-bootstrap';
import SongScoring from './SongScoring';
class Assess extends Component {
constructor(props) {
super(props);
const songs = [
{
track: "song1",
},
{
track: "song2",
},
{
track: "song3",
},
{
track: "song4",
},
]
this.songScoring = [];
const onChange = [];
const onChangeActive = [];
for (let i = 0; i < songs.length; i++) {
this.songScoring.push(React.createRef());
onChange.push(function (event, newScore) {
console.log("onChange: ", this);
});
onChangeActive.push(function (event, newHover) {
console.log("onChangeActive: ", this);
});
}
this.state = {
songs: songs,
onChange: onChange,
onChangeActive: onChangeActive
};
}
componentDidMount() {
console.log(this.state.songScoring);
const onChange = [];
const onChangeActive = [];
for (let i = 0; i < this.state.songs.length; i++) {
onChange.push(this.state.onChange[i].bind(this.songScoring[i], 1));
onChangeActive.push(this.state.onChangeActive[i].bind(this.songScoring[i]));
}
this.setState({
onChange: onChange,
onChangeActive: onChangeActive
});//, () => this.setState({unicorn: 1}));
}
render() {
return (
<div className="root">
<Accordion defaultActiveKey="0">
{
this.state.songs.map((song, index) => (
<SongScoring song={song} ref={this.songScoring[index]} key={index.toString()} index={index} onChange={this.state.onChange[index]} onChangeActive={this.state.onChangeActive[index]} />
))
}
</Accordion>
</div>
);
}
}
export default Assess;
Child code:
import React, { Component } from 'react';
import './SongScoring.css';
import { Accordion, Card, Container, Row } from 'react-bootstrap';
import ReactPlayer from "react-player";
import Rating from '#material-ui/lab/Rating';
import Box from '#material-ui/core/Box';
const labels = {
0.5: 'Unpleasant',
1: 'Bearable',
1.5: 'Bearable+',
2: 'Intriguing',
2.5: 'Intriguing+',
3: 'Ok',
3.5: 'Ok+',
4: 'Pleasant',
4.5: 'Pleasant+',
5: 'Excellent',
};
class SongScoring extends Component {
constructor(props) {
super(props);
this.state = {
song: props.song,
key: props.index.toString(),
score: props.song.score || 0,
hover: props.song.score || -1,
onChange: this.props.onChange,
onChangeActive: this.props.onChangeActive
}
}
componentDidUpdate(prevProps, prevState){
if (prevProps.onChange !== this.state.onChange){
this.setState({
onChange: prevProps.onChange,
onChangeActive: prevProps.onChangeActive
})
}
}
render() {
return (
<>
<Card>
<Accordion.Toggle as={Card.Header} variant="link" eventKey={this.state.key}>
{this.state.key}
<Rating name="read-only" value={this.state.score} precision={0.5} readOnly />
</Accordion.Toggle>
<Accordion.Collapse eventKey={this.state.key}>
<Card.Body style={{ display: 'flex', alignItems: 'center', lineHeight: '1' }}>
<Container>
<Row className='scoring-row'>
<ReactPlayer
url="https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3"
width="400px"
height="50px"
playing={false}
controls={true}
style={{outline: 'none'}}
/>
</Row>
<Row className='scoring-row'>
<Rating
name="song-rating"
value={this.state.score}
precision={0.5}
onChange={this.state.onChange}
onChangeActive={this.state.onChangeActive}
style={{ marginTop: '40px' }}
/>
</Row>
<Row className='scoring-row'>
{this.state.score !== 0 && <Box>{labels[this.state.hover !== -1 ? this.state.hover : this.state.score]}</Box>}
</Row>
</Container>
</Card.Body>
</Accordion.Collapse>
</Card>
</>
);
}
}
export default SongScoring;
In this setting, console.log in onChange and onChangeActive output both 'undefined'.
Now, if I uncomment the fragment: '//, () => this.setState({unicorn: 1}));' (unicorn is a dummy, unused variable) in the parent, console.log of onChange and onChangeActive prints out nicely 'this' as the references for respective SongScoring components.
Therefore my questions are:
what's going on here, in both problems? Is it a bug of the 'Rating' component?
how to solve my initial problem more efficiently than by using the second approach with the uncommented fragment?
UPDATE: I easily fixed the first issue by providing unique names for all the 'Rating' components, but the question about 2 x setState fixing props remains open.

Material UI - tooltip displays unexpectedly

I use material-ui with react in my project. What I want to do is to simply change the tooltip (each tooltip contains an Icon) when certain condition is met.
ToolTipWrapper.js
import React from "react";
import { Tooltip } from "#material-ui/core";
import { CheckCircle, Error } from "#material-ui/icons";
import { green, deepOrange } from "#material-ui/core/colors";
class TooltipWrapper extends React.Component {
render() {
return this.props.error.length === 0 ? (
<Tooltip title="meet all requirements" placement="bottom-start">
<CheckCircle style={{ color: green[400] }} />
</Tooltip>
) : (
<Tooltip title="Not meet all requirements" placement="bottom-start">
<Error style={{ color: deepOrange[400] }} />
</Tooltip>
);
}
}
export default TooltipWrapper;
index.js
import React from "react";
import { render } from "react-dom";
import TooltipWrapper from "./TooltipWrapper.js";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
error: []
};
}
handleClick = () => {
this.setState({
error: [123]
});
};
render() {
return (
<div style={styles}>
<TooltipWrapper error={this.state.error} />
<button onClick={this.handleClick}>change</button>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Please check this demo in codesandbox:demo link
Now the problem is that the second tooltip will not show as expected after changing from the first tooltip by clicking the button. The position of the second tooltip magically moved to the top left, which is quite strange.
Thanks for your help.
try this.
import React from "react";
import { Tooltip } from "#material-ui/core";
import { CheckCircle, Error } from "#material-ui/icons";
import { green, deepOrange } from "#material-ui/core/colors";
class TooltipWrapper extends React.Component {
render() {
return (
<Tooltip
title={
this.props.error.length === 0
? "meet all requirements"
: "Not meet all requirements"
}
placement="bottom-start"
id="test"
>
<span>
{this.props.error.length === 0 ? (
<CheckCircle style={{ color: green[400] }} aria-label="test" />
) : (
<Error style={{ color: deepOrange[400] }} aria-label="test" />
)}
</span>
</Tooltip>
);
}
}
export default TooltipWrapper;
working sandbox version.
https://codesandbox.io/s/w2zkn847m5

How to passing data value from map to Material UI ButtonBase onClick event

I have an array data that rendered with map and wrapped with Material Ui ButtonBase component. I want to get the data name if button is clicked.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Card, { CardContent } from 'material-ui/Card';
import Typography from 'material-ui/Typography';
import ButtonBase from "material-ui/ButtonBase";
const styles = theme => ({
card: {
textAlign: 'center',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
cardButton: {
display: "block",
textAlign: "initial",
}
});
class SimpleMediaCard extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{
name: 'Javascript',
image: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQGLecy3onT9rChTsw1BawUkuOxKE1r6yx_ViYXjqpBq8zsE_BI'
},
{
name: 'React',
image: 'https://lapraim.com/assets/partners/react-logo.png'
}
]
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(evt) {
alert('Name : ');
}
render() {
const { classes } = this.props;
const { data } = this.state;
const { handleClick } = this;
return (
<div>
{data.map(function(item, i) {
return (
<div key={i}>
<ButtonBase className={classes.cardButton} onClick={handleClick}>
<Card className={classes.card}>
<img src={item.image} height='100px' alt={item.name}/>
<CardContent>
<Typography component="h2">
{item.name}
</Typography>
</CardContent>
</Card>
</ButtonBase>
</div>
)
})}
</div>
)
}
}
SimpleMediaCard.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SimpleMediaCard);
This is the demo code link
At code above if picture is clicked it will show an alert.
I want to show the data name on each clicked picture.
Can anyone help me to show data name every picture is clicked?
You mean something like this?
handleClick(item) {
alert('Name : ' + item.name);
}
<ButtonBase className={classes.cardButton} onClick={() => handleClick(item)}>
Just pass the item to the event handler.

Changing the url in React/Redux

I'm trying to build an app with React and Redux (DVA). It's using Ant.Design as the main framework. I'm trying to change the URL when the user clicks on a button, and obviously 'bind' that url change to an action, so that if the user goes directly to that URL, he gets what he wants.
At the moment here's what I have, in a function in my component.
const { dispatch, match } = this.props;
dispatch(routerRedux.push('/f/' + record.id));
This is the only thing that I was able to produce. It correctly changes the url, but doesn't bind the url with a specific behaviour, making it completely useless.
How do I link the URL with an action?
If you wish to trigger an action based on a URL, you'll need to use react-router to route a component that then performs the desired action. In such a case it is also a good idea to then visit a different URL, erasing the action-URL from the browser's history.
A typical router definition might look something like this (taken from react-router-redux's docs):
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/success" component={Success}/>
</div>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
)
So you wish to add a path /f/<record-id>. You can do this by adding a line like this:
<Route path="/f/:recordId" component={MyActionComponent}/>
Now you need to define a component, MyActionComponent, that will perform your action.
import { connect } from 'react-redux';
import { replace } from 'react-router-redux';
const mapDispatchToProps = (dispatch: Dispatch) => ({
visitNextLocation: () => dispatch(replace('/success')),
myAction: (recordId) => dispatch(myAction(recordId)),
});
const withDispatch = connect(null, mapDispatchToProps);
class MyActionComponent extends Component {
props: {
match: {
params: {
recordId: string,
}
},
redirectToLogin: () => void,
myAction: string => void,
};
componentWillMount() {
const recordId = this.props.match.params.recordId;
if (recordId) {
this.props.myAction(token);
this.props.visitNextLocation();
}
}
render() {
return null;
}
}
Note the use of replace instead of push. This means, when a user visits this URL their action will get performed and they'll end up on /success. But if they click the Back button, they won't then revisit this URL and run the action again.
I can't put the code on Codepen for privacy reasons. But here's an extract:
router.js
...
},
'/users': {
component: dynamicWrapper(app, ['rule'], () => import('../routes/users')),
},
'/f/:userID': {
component: dynamicWrapper(app, ['rule'], () => import('../routes/users')),
},
...
users.js (the main component that contains LeftPanel and RightPanel)
import React, { PureComponent } from 'react';
import { connect } from 'dva';
import { Row, Col, Card, List, Divider, Badge, Select, Radio, Input, Popover, Button, Table, Spin } from 'antd';
import RightPanel from './RightPanel';
import LeftPanel from './LeftPanel';
import { routerRedux, Route, Switch } from 'dva/router';
import 'font-awesome/css/font-awesome.min.css';
import FadeIn from 'react-fade-in';
#connect(({ rule, loading }) => ({rule, loading: loading.models.rule }))
export default class Users extends React.Component {
constructor(props) {
super(props)
this.state = {
selected_user: [],
defaultView: true,
isLoadingNow: false,
selectedRowKeys: [],
makeEmpty: false,
searchRes: []
}
}
selectRow = (record) => {
const { dispatch, match } = this.props;
dispatch(routerRedux.replace({ pathname: '/f/' + record.id }));
this.setState({isLoadingNow: true, selectedRowKeys: record.key})
setTimeout(() => {
this.setState({
isLoadingNow: false,
defaultView: false,
selected_user: record
})
}, 75)
}
componentDidMount() {
const { dispatch, match } = this.props;
dispatch({
type: 'rule/fetch'
});
if (match.params.userID == undefined) {
// do nothing
} else if (match.params.userID) {
var result = this.props.rule.data.list.filter(function( obj ) {
return obj.id == match.params.userID;
});
this.selectRow.bind(this, result[0])
}
}
render() {
const { selectedRowKeys } = this.state;
const rowSelection = {
selectedRowKeys,
type:"radio"
};
const { rule: { data }, loading } = this.props;
return (<div>
<LeftPanel
rowSelection={rowSelection}
dataSource={this.state.makeEmpty ? this.state.searchRes : this.props.rule.data.list}
selectRow={this.selectRow}
loadingStatus={loading}
/>
<RightPanel
selected_user={this.state.selected_user}
is_default={this.state.defaultView}
loading={this.state.isLoadingNow}
/>
</div>);
}
}
leftPanel.js (responsible for displaying the list of links, on which the user will click on one, which will do 2 things:
- change the url accordingly
- display specific data on RightPanel.js)
import React from 'react';
import { Table, Card } from 'antd';
import styles from './index.less';
// import 'font-awesome/css/font-awesome.min.css';
import { Row, Col, List, Divider, Badge, Select, Radio, Input, Popover, Button } from 'antd';
var moment = require('moment');
class LeftPanel extends React.Component {
constructor(props) {
super(props)
this.state = {
selected_row_index: undefined
}
}
handleChangeStyleOnSelectRow(index) {
this.setState({
selected_row_index: index
}, console.log(this.state.selected_row_index))
}
prettifyForTable(raw_data) {
var prettyRows = [];
raw_data.map((item,index) =>
prettyRows.push(
<div style={{"width": "100%"}}>
<Row style={{ "align-items": "center"}} type="flex" justify="space-between">
<Col span={10}>
<div style={{"font-size": "15px", "text-align": "center"}}>
{item.user_name} <i style={{"color": "rgba(0, 0, 0, 0.25)", "margin": "0 10px", "transform": "rotate(45deg)"}} className="fa fa-plane"> </i> {item.user_age}
<div style={{"font-size": "12px", "color": "grey"}}> {moment(item.user_color).format('HH:MM')} · {moment(item.user_order).format('HH:MM')} </div>
</div>
</Col>
<Col span={3}>
<div style={{"text-align": "right", "text-align": "center"}}>
{item.user_family}
</div>
</Col>
<Col span={6}>
<div style={{"text-align": "right", "text-align": "center"}}>
{moment(item.user_height).format('MMMM D')}
</div>
</Col>
<Col span={3}>
<div style={{"text-align": "center"}}>
{(item.status == "in_progress") ? <div> <Badge style={{"padding-right": "25px"}} status="processing"/></div> : <div style={{"text-align": "center"}}> <Badge style={{"padding-right": "25px"}} status="default"/></div>}
</div>
</Col>
</Row>
</div>
)
);
return prettyRows;
}
render() {
const stylesSelectedRow = { "background": "rgba(155,155,155,0.05)", "box-shadow": "0 0 5px 0 #4A90E2", "transform": "scale(1.01)"};
const { dataSource } = this.props;
return(
<div>
{dataSource &&
<Card bordered={false} loading={this.props.loadingStatus} className={styles.userRows} bodyStyle={{"padding": "0 15px"}}>
<List
size="small"
bordered={false}
dataSource={this.prettifyForTable(dataSource)}
renderItem={(item, index) => (<List.Item onClick={() => {this.state.selected_row_index == index ? null : this.props.selectRow(this.props.dataSource[index]); this.handleChangeStyleOnSelectRow(index)}} style={this.state.selected_row_index == index ? stylesSelectedRow : null} className={styles.userRows}>{item}</List.Item>)}
/>
</Card>
}
</div>
)
}
}
export default LeftPanel;
and finally RightPanel.js, that is reponsible for listening to the URL or a click on LeftPanel, and display data accordingly.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import infopng from '../../../assets/info.svg';
import TabInformations from './TabInformations.js';
import TabFamily from './TabFamily.js';
import TabProblems from './TabProblems.js';
import { Tabs, Button, Spin, Icon, Table, Pagination, Card, Col, Row, Spinner, Badge } from 'antd';
const TabPane = Tabs.TabPane;
import 'font-awesome/css/font-awesome.min.css';
import WindowSizeListener from 'react-window-size-listener'
import FadeIn from 'react-fade-in';
export default class RightPanel extends Component {
render() {
if (this.props.loading) {
return(
<div>
<Spin
indicator={<Icon type="loading" style={{ fontSize: 24 }} spin />}
src="http://web.gndu.ac.in/DepartmentProfile/images/spinner.gif"
/>
</div>
);
} else if (this.props.is_default) {
return(
<div style={{"margin-top": "64px", "margin-right": "10px", "text-align": "center", "height": "90vh", "display": "flex", "align-items": "center", "justify-content": "center"}}>
<div>
<img src={infopng} style={{"height": "155px"}} />
<p style={{"color": "#8e8e8e"}}> select a user on the <br/> left-hand side... </p>
</div>
</div>
);
} else {
return (
<FadeIn>
<Card bodyStyle={{"padding": "0"}} style={{"background-color": "white", "height":"90vh", "padding": "20px", "box-shadow": "rgba(0, 21, 41, 0.1) 0px 0px 6px", "opacity": "1", "transition": "background 0.6s", "border-radius": "2px", "margin": "10px 10px 0 0px", "margin-top": "64px"}}>
<Tabs defaultActiveKey="1" style={{"text-align": "center", "padding": "0 15px"}}>
<TabPane tab="General" key="1">
<TabInformations
selected_user={this.props.selected_user}
/>
</TabPane>
<TabPane tab="Servicing" key="2">
<TabFamily
selected_user={this.props.selected_user}
/>
</TabPane>
<TabPane tab={<div>Defect(s)<Badge showZero count={0} style={{ backgroundColor: '#fff', color: '#999', boxShadow: '0 0 0 1px #d9d9d9 inset', "margin-left": "10px" }} /></div>} key="3">
<TabProblems />
</TabPane>
</Tabs>
</Card>
</FadeIn>
);
}
}
}
This code does the initial job pretty well: when the user clicks on a link on leftPanel.js, leftPanel.js calls the method selectRow in users.js, which in turn selects a row and display it on RightPanel.js.
My question: how to add to this the fact that it changes the URL whenever the user clicks? And obviously, if the user clicks on "go back" in Chrome, the data in RightPanel.js has to change accordingly.. ?

Calling a method from a different component (File) in React JS

I have an App Bar component, I'm using Material UI v1.0.0-beta.33
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "material-ui/styles";
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";
import TemporaryDrawer from "./Drawer";
const styles = {
root: {
width: "100%"
},
flex: {
flex: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
};
function ButtonAppBar(props) {
const { classes } = props;
return (
<div className={classes.root}>
<TemporaryDrawer/>
<AppBar position="static">
<Toolbar>
<IconButton className={classes.menuButton} color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" className={classes.flex}>
Title
</Typography>
<Button onClick={} color="inherit">User Name</Button>
</Toolbar>
</AppBar>
</div>
);
}
ButtonAppBar.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(ButtonAppBar);
As you can see I'm importing another component called TemporaryDrawer, in the code of that component there's a method called "toggleDrawer" that triggers the Drawer.
My questions is how can I use the toggleDrawer method from TemporaryDrawer in the above code, I have a button with the onClick method empty.
For reference, I put the code from TemporaryDrawer below:
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "material-ui/styles";
import Drawer from "material-ui/Drawer";
import List from "material-ui/List";
import Divider from "material-ui/Divider";
const styles = {
list: {
width: 250
},
listFull: {
width: "auto"
}
};
class TemporaryDrawer extends React.Component {
state = {
left: false
};
toggleDrawer = (side, open) => () => {
this.setState({
[side]: open
});
};
render() {
const { classes } = this.props;
const sideList = (
<div className={classes.list}>
<List>AA</List>
<List>BB</List>
<List>CC</List>
<Divider />
<List>AA1</List>
<List>BB1</List>
<List>CB1</List>
</div>
);
return (
<div>
<Drawer open={this.state.left} onClose={this.toggleDrawer("left", false)}>
<div
tabIndex={0}
role="button"
onClick={this.toggleDrawer("left", false)}
onKeyDown={this.toggleDrawer("left", false)}
>
{sideList}
</div>
</Drawer>
</div>
);
}
}
TemporaryDrawer.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(TemporaryDrawer);
Thanks in advance.
Looking at the toggleDrawer method in TemporaryDrawer component
the method can simply be "litfed" into the parent component ButtonAppBar
updated ButtonAppBar turned in the class component:
class ButtonAppBar extends Component {
constructor(props) {
super(props);
this.state {
isDrawerOpen: false
}
this.closeDrawer = this.closeDrawer.bind(this);
}
closeDrawer() {
this.setState({ isDrawerOpen: false });
}
render() {
const { classes } = props;
return (
<div className={classes.root}>
<TemporaryDrawer open={this.state.isDrawerOpen} onDrawerClose={this.closeDrawer} />
<AppBar position="static">
<Toolbar>
<IconButton className={classes.menuButton} color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" className={classes.flex}></Typography>
<Button onClick={} color="inherit">User Name</Button>
</Toolbar>
</AppBar>
</div>
);
}
}
And then TemporaryDrawer component can be:
class TemporaryDrawer extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Drawer open={this.props.isDrawerOpen} onClose={this.props.onDrawerClose}>
...
</Drawer>
)
}
}

Categories