最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ReactJS TypeError: this.props. is not a function - Stack Overflow

programmeradmin3浏览0评论

I'm building an application, where there is a form presented with different steps. In all the steps but one, I manage to provide the necessary functions as props to make some operations such as 'handleNext', 'handleBack' or 'handleChange'.

Nevertheless, in the last step, represented in the class SuccessForm, when I try to execute the 'handleDownload' function, I get the following error:

TypeError: this.props.handleDownload is not a function

Here it is the SuccessForm.js class:

export class SuccessForm extends Component {

    constructor() {
        super();
    }

    download = e => {
        e.preventDefault();
        this.props.handleDownload();
    }

    render() {
        return (
            <React.Fragment>
                <Grid container>
                    <Grid item xs={12} sm={2}>
                        <DirectionsWalkIcon fontSize="large" style={{
                            fill: "orange", width: 65,
                            height: 65
                        }} />
                    </Grid>
                    <Grid>
                        <Grid item xs={12} sm={6}>
                            <Typography variant="h5" gutterBottom>
                                Route created
                        </Typography>
                        </Grid>
                        <Grid item xs={12}>
                            <Typography variant="subtitle1">
                                Your new track was succesfully created and saved
                        </Typography>
                        </Grid>
                    </Grid>
                    <Tooltip title="Download" arrow>
                        <IconButton
                            variant="contained"
                            color="primary"
                            style={{
                                marginLeft: 'auto',
                                // marginRight: '2vh'
                            }}
                            onClick={this.download}
                        >
                            <GetAppIcon fontSize="large" style={{ fill: "orange" }} />
                        </IconButton>
                    </Tooltip>
                </Grid>
            </React.Fragment>
        )
    }
}

The entire NewRouteForm.js:

import React, { Component } from 'react'
import { makeStyles, MuiThemeProvider } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import DataForm from '../stepper/dataform/DataForm';
import ReviewForm from '../stepper/reviewform/ReviewForm';
import MapForm from '../stepper/mapform/MapForm';
import NavBar from '../../graphic interface/NavBar';
import DirectionsWalkIcon from '@material-ui/icons/DirectionsWalk';
import Avatar from '@material-ui/core/Avatar';
import CheckCircleOutlineOutlinedIcon from '@material-    ui/icons/CheckCircleOutlineOutlined';
import FilterHdrIcon from '@material-ui/icons/FilterHdr';
import Grid from '@material-ui/core/Grid';
import SuccessForm from '../stepper/success/SuccessForm';
import { withStyles } from '@material-ui/styles';


export class NewRouteForm extends Component {

state = {
    activeStep: 0,
    name: '',
    description: '',
    date: new Date(),
    photos: [],
    videos: [],
    points: []
};

handleNext = () => {
    const { activeStep } = this.state;
    this.setState({ activeStep: activeStep + 1 });
};

handleBack = () => {
    const { activeStep } = this.state;
    this.setState({ activeStep: activeStep - 1 });
};

handleChange = input => e => {
    this.setState({ [input]: e.target.value });
}

handleDateChange = date => {
    this.setState({ date: date });
}

handleMediaChange = (selectorFiles: FileList, code) => { // this is not an error, is TypeScript
    switch (code) {
        case 0: // photos
            this.setState({ photos: selectorFiles });
            break;
        case 1: // videos
            this.setState({ videos: selectorFiles });
            break;
        default:
            alert('Invalid media code!!');
            console.log(code)
            break;
    }
}

handleMapPoints = points => {
    this.setState({ points: points })
}

// ###########################
// Download and Upload methods
// ###########################

handleDownload = () => {
    // download route
    console.log("DOWNLOAD")
    alert("DOWNLOAD");
}

upload = () => {
    // upload route
}

render() {

    const { activeStep } = this.state;
    const { name, description, date, photos, videos, points } = this.state;
    const values = { activeStep, name, description, date, photos, videos, points };

    const { classes } = this.props;

    return (
        <MuiThemeProvider>
            <React.Fragment>
                <NavBar />
                <main className={classes.layout}>
                    <Paper className={classes.paper}>
                        <Avatar className={classes.avatar}>
                            <FilterHdrIcon fontSize="large" />
                        </Avatar>

                        <Typography ponent="h1" variant="h4" align="center">
                            Create your own route
                        </Typography>

                        <Stepper activeStep={activeStep} className={classes.stepper}>
                            {steps.map((label) => (
                                <Step key={label}>
                                    <StepLabel>{label}</StepLabel>
                                </Step>
                            ))}
                        </Stepper>

                        <React.Fragment>
                            {activeStep === steps.length ? (
                                <SuccessForm />
                            ) : (
                                    <React.Fragment>
                                        {getStepContent(activeStep,
                                            values,
                                            this.handleNext,
                                            this.handleBack,
                                            this.handleChange,
                                            this.handleDateChange,
                                            this.handleMediaChange,
                                            this.handleMapPoints,
                                            this.handleDownload
                                        )}

                                    </React.Fragment>
                                )}
                        </React.Fragment>
                    </Paper>
                </main>
            </React.Fragment>
        </MuiThemeProvider>
    )
}
}

const steps = ['Basic data', 'Map', 'Review your route'];

function getStepContent(step, 
                    values, 
                    handleNext, 
                    handleBack, 
                    handleChange, 
                    handleDateChange, 
                    handleMediaChange, 
                    handleMapPoints, 
                    handleDownload) {
switch (step) {
    case 0:
        return <DataForm
            handleNext={handleNext}
            handleChange={handleChange}
            handleDateChange={handleDateChange}
            handleMediaChange={handleMediaChange}
            values={values}
        />;
    case 1:
        return <MapForm
            handleNext={handleNext}
            handleBack={handleBack}
            handleMapPoints={handleMapPoints}
            values={values}
        />;
    case 2:
        return <ReviewForm
            handleNext={handleNext}
            handleBack={handleBack}
            values={values}
        />;
    case 3:
        return <SuccessForm
            handleDownload={handleDownload}
        />;
    default:
        throw new Error('Unknown step');
}
}

const useStyles = theme => ({
layout: {
    width: 'auto',
    marginLeft: theme.spacing(2),
    marginRight: theme.spacing(2),
    [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {
        width: 600,
        marginLeft: 'auto',
        marginRight: 'auto',
    },
},
paper: {
    marginTop: theme.spacing(3),
    marginBottom: theme.spacing(3),
    padding: theme.spacing(2),
    [theme.breakpoints.up(600 + theme.spacing(3) * 2)]: {
        marginTop: theme.spacing(6),
        marginBottom: theme.spacing(6),
        padding: theme.spacing(3),
    },
},
stepper: {
    padding: theme.spacing(3, 0, 5),
},
buttons: {
    display: 'flex',
    justifyContent: 'flex-end',
},
button: {
    marginTop: theme.spacing(3),
    marginLeft: theme.spacing(1),
},
avatar: {
    marginLeft: 'auto',
    marginRight: 'auto',
    backgroundColor: theme.palette.warning.main,
},
icon: {
    width: 65,
    height: 65,
},
grid: {
    marginLeft: theme.spacing(2),
}
});

export default withStyles(useStyles)(NewRouteForm);

I'm building an application, where there is a form presented with different steps. In all the steps but one, I manage to provide the necessary functions as props to make some operations such as 'handleNext', 'handleBack' or 'handleChange'.

Nevertheless, in the last step, represented in the class SuccessForm, when I try to execute the 'handleDownload' function, I get the following error:

TypeError: this.props.handleDownload is not a function

Here it is the SuccessForm.js class:

export class SuccessForm extends Component {

    constructor() {
        super();
    }

    download = e => {
        e.preventDefault();
        this.props.handleDownload();
    }

    render() {
        return (
            <React.Fragment>
                <Grid container>
                    <Grid item xs={12} sm={2}>
                        <DirectionsWalkIcon fontSize="large" style={{
                            fill: "orange", width: 65,
                            height: 65
                        }} />
                    </Grid>
                    <Grid>
                        <Grid item xs={12} sm={6}>
                            <Typography variant="h5" gutterBottom>
                                Route created
                        </Typography>
                        </Grid>
                        <Grid item xs={12}>
                            <Typography variant="subtitle1">
                                Your new track was succesfully created and saved
                        </Typography>
                        </Grid>
                    </Grid>
                    <Tooltip title="Download" arrow>
                        <IconButton
                            variant="contained"
                            color="primary"
                            style={{
                                marginLeft: 'auto',
                                // marginRight: '2vh'
                            }}
                            onClick={this.download}
                        >
                            <GetAppIcon fontSize="large" style={{ fill: "orange" }} />
                        </IconButton>
                    </Tooltip>
                </Grid>
            </React.Fragment>
        )
    }
}

The entire NewRouteForm.js:

import React, { Component } from 'react'
import { makeStyles, MuiThemeProvider } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import DataForm from '../stepper/dataform/DataForm';
import ReviewForm from '../stepper/reviewform/ReviewForm';
import MapForm from '../stepper/mapform/MapForm';
import NavBar from '../../graphic interface/NavBar';
import DirectionsWalkIcon from '@material-ui/icons/DirectionsWalk';
import Avatar from '@material-ui/core/Avatar';
import CheckCircleOutlineOutlinedIcon from '@material-    ui/icons/CheckCircleOutlineOutlined';
import FilterHdrIcon from '@material-ui/icons/FilterHdr';
import Grid from '@material-ui/core/Grid';
import SuccessForm from '../stepper/success/SuccessForm';
import { withStyles } from '@material-ui/styles';


export class NewRouteForm extends Component {

state = {
    activeStep: 0,
    name: '',
    description: '',
    date: new Date(),
    photos: [],
    videos: [],
    points: []
};

handleNext = () => {
    const { activeStep } = this.state;
    this.setState({ activeStep: activeStep + 1 });
};

handleBack = () => {
    const { activeStep } = this.state;
    this.setState({ activeStep: activeStep - 1 });
};

handleChange = input => e => {
    this.setState({ [input]: e.target.value });
}

handleDateChange = date => {
    this.setState({ date: date });
}

handleMediaChange = (selectorFiles: FileList, code) => { // this is not an error, is TypeScript
    switch (code) {
        case 0: // photos
            this.setState({ photos: selectorFiles });
            break;
        case 1: // videos
            this.setState({ videos: selectorFiles });
            break;
        default:
            alert('Invalid media code!!');
            console.log(code)
            break;
    }
}

handleMapPoints = points => {
    this.setState({ points: points })
}

// ###########################
// Download and Upload methods
// ###########################

handleDownload = () => {
    // download route
    console.log("DOWNLOAD")
    alert("DOWNLOAD");
}

upload = () => {
    // upload route
}

render() {

    const { activeStep } = this.state;
    const { name, description, date, photos, videos, points } = this.state;
    const values = { activeStep, name, description, date, photos, videos, points };

    const { classes } = this.props;

    return (
        <MuiThemeProvider>
            <React.Fragment>
                <NavBar />
                <main className={classes.layout}>
                    <Paper className={classes.paper}>
                        <Avatar className={classes.avatar}>
                            <FilterHdrIcon fontSize="large" />
                        </Avatar>

                        <Typography ponent="h1" variant="h4" align="center">
                            Create your own route
                        </Typography>

                        <Stepper activeStep={activeStep} className={classes.stepper}>
                            {steps.map((label) => (
                                <Step key={label}>
                                    <StepLabel>{label}</StepLabel>
                                </Step>
                            ))}
                        </Stepper>

                        <React.Fragment>
                            {activeStep === steps.length ? (
                                <SuccessForm />
                            ) : (
                                    <React.Fragment>
                                        {getStepContent(activeStep,
                                            values,
                                            this.handleNext,
                                            this.handleBack,
                                            this.handleChange,
                                            this.handleDateChange,
                                            this.handleMediaChange,
                                            this.handleMapPoints,
                                            this.handleDownload
                                        )}

                                    </React.Fragment>
                                )}
                        </React.Fragment>
                    </Paper>
                </main>
            </React.Fragment>
        </MuiThemeProvider>
    )
}
}

const steps = ['Basic data', 'Map', 'Review your route'];

function getStepContent(step, 
                    values, 
                    handleNext, 
                    handleBack, 
                    handleChange, 
                    handleDateChange, 
                    handleMediaChange, 
                    handleMapPoints, 
                    handleDownload) {
switch (step) {
    case 0:
        return <DataForm
            handleNext={handleNext}
            handleChange={handleChange}
            handleDateChange={handleDateChange}
            handleMediaChange={handleMediaChange}
            values={values}
        />;
    case 1:
        return <MapForm
            handleNext={handleNext}
            handleBack={handleBack}
            handleMapPoints={handleMapPoints}
            values={values}
        />;
    case 2:
        return <ReviewForm
            handleNext={handleNext}
            handleBack={handleBack}
            values={values}
        />;
    case 3:
        return <SuccessForm
            handleDownload={handleDownload}
        />;
    default:
        throw new Error('Unknown step');
}
}

const useStyles = theme => ({
layout: {
    width: 'auto',
    marginLeft: theme.spacing(2),
    marginRight: theme.spacing(2),
    [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {
        width: 600,
        marginLeft: 'auto',
        marginRight: 'auto',
    },
},
paper: {
    marginTop: theme.spacing(3),
    marginBottom: theme.spacing(3),
    padding: theme.spacing(2),
    [theme.breakpoints.up(600 + theme.spacing(3) * 2)]: {
        marginTop: theme.spacing(6),
        marginBottom: theme.spacing(6),
        padding: theme.spacing(3),
    },
},
stepper: {
    padding: theme.spacing(3, 0, 5),
},
buttons: {
    display: 'flex',
    justifyContent: 'flex-end',
},
button: {
    marginTop: theme.spacing(3),
    marginLeft: theme.spacing(1),
},
avatar: {
    marginLeft: 'auto',
    marginRight: 'auto',
    backgroundColor: theme.palette.warning.main,
},
icon: {
    width: 65,
    height: 65,
},
grid: {
    marginLeft: theme.spacing(2),
}
});

export default withStyles(useStyles)(NewRouteForm);
Share Improve this question edited Mar 30, 2020 at 18:26 Pablo Cañal Suárez asked Mar 30, 2020 at 17:51 Pablo Cañal SuárezPablo Cañal Suárez 2351 gold badge4 silver badges11 bronze badges 3
  • okay, finally we need that NewDataForm.js file, that's the important one because there is where you define your functions – Edgar Henriquez Commented Mar 30, 2020 at 18:11
  • @EdgarHenriquez done. Sorry for the confusion. – Pablo Cañal Suárez Commented Mar 30, 2020 at 18:17
  • You also have a bug on the last condition... check the updated answer – Dennis Vash Commented Mar 30, 2020 at 18:21
Add a ment  | 

2 Answers 2

Reset to default 4

Try calling super(props) in the constructor:

constructor(props) {
  super(props);
}

and passing function with this instance (this.handleDownload) as it is a class property:

<SuccessForm handleDownload={this.handleDownload} />

Update:

You have a bug on the last step when you not passing a property:

activeStep === steps.length ? <SuccessForm handleDownload={this.handleDownload}/>

Assuming that you have a class in your parent Component, what you're missing is the this keyword in the function reference...

case 3:
     return <SuccessForm
         handleDownload={this.handleDownload}
     />;
发布评论

评论列表(0)

  1. 暂无评论