简体   繁体   中英

React: TypeError: this.props.update is not a function

Let me begin by saying that I'm very new to programming as a whole so please bare with me if i ask stupid questions.

Basically, I'm trying to add a date picker to a search application in react, I have the UI there, and I can pre-set a date range in the code. However I want the user to be able to select a start and end date for the search results.

I know my code is incomplete and I know where I am going wrong, but I just don't know how to 'fix' it.

Datepicker:

import React from "react";
import "date-fns";

import { Paper } from "@material-ui/core";

import DateFnsUtils from "@date-io/date-fns";
import {
  MuiPickersUtilsProvider,
  KeyboardDatePicker,
} from "@material-ui/pickers";

class DatePickerWidget extends React.Component {
  render() {
    return (
      <Paper id="date-picker-widget" elevation={8}>
        <Paper id="date-filters" elevation={8}>
          <MuiPickersUtilsProvider utils={DateFnsUtils}>
            <KeyboardDatePicker
              margin="normal"
              id="date-picker-dialog"
              label="Start Date"
              format="yyyy/MM/dd"
              value={this.props.startDate}
              maxDate={this.props.endDate}
              onChange={(date) => this.props.update({ startDate: date })}
              KeyboardButtonProps={{
                "aria-label": "change start date",
              }}
            />
            <KeyboardDatePicker
              margin="normal"
              id="date-picker-dialog"
              label="End Date"
              format="yyyy/MM/dd"
              value={this.props.endDate}
              minDate={this.props.startDate}
              onChange={(date) => this.props.update({ endDate: date })}
              KeyboardButtonProps={{
                "aria-label": "change end date",
              }}
            />
          </MuiPickersUtilsProvider>
        </Paper>
      </Paper>
    );
  }
}

export default DatePickerWidget;

Search box where the date picker is displayed:

    import React from "react";

import { TextField, Button, Typography } from "@material-ui/core";

import "./SearchBox.css"
import DatePickerWidget from "./DatePickerWidget"

class SearchBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { searchTerm: "", results: "" };

    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.validateSearch = this.validateSearch.bind(this);
    this.clear = this.clear.bind(this);

  
  }

  componentDidMount() {
    this.setState({ searchTerm: this.props.searchTerm });
  }
  componentDidUpdate(prevProps) {
    if (prevProps.searchTerm !== this.props.searchTerm)
      this.setState({ searchTerm: this.props.searchTerm });
  }

  handleChange(event) {
    this.setState({ [event.target.name]: event.target.value });
  }

  handleSubmit(event) {
    event.preventDefault();
    this.props.update({ searchTerm: this.state.searchTerm });
    this.setState({ results: this.state.searchTerm });
  }

  validateSearch() {
    return this.state.searchTerm !== "";
  }

  clear() {
    this.setState({ searchTerm: "", results: "" });
    this.props.update({ searchTerm: "" });
  }

  render() {
    return (
      <React.Fragment>
        <form id="search-form" onSubmit={this.handleSubmit}>
          <Button
            style={{ width: "40%" }}
            variant="outlined"
            color="primary"
            onClick={this.clear}
            disabled={this.state.results === ""}
          >
            Clear
          </Button>
          <TextField
            style={{ flexBasis: "100%" }}
            variant="outlined"
            label="Search Term"
            type="search"
            name="searchTerm"
            value={this.state.searchTerm}
            onChange={this.handleChange}
          />
          <Button
            style={{ width: "40%" }}
            variant="contained"
            color="primary"
            disabled={!this.validateSearch()}
            type="submit"
          >
            Search
          </Button>


          <DatePickerWidget startDate="2020/09/01" endDate="2020/09/30"/>
        </form>

        
        {this.state.results && (
          <Typography align="center" variant="body1">
            Displaying results for: {this.state.results}
          </Typography>
        )}
      </React.Fragment>
    );
  }
}


export default SearchBox;

The error I'm getting when I change the dates on the interface:

TypeError: this.props.update is not a function

          format="yyyy/MM/dd"
          value={this.props.startDate}
          maxDate={this.props.endDate}
        > onChange={(date) => this.props.update({ startDate: date })}
          KeyboardButtonProps={{
            "aria-label": "change start date",

I'm fairly certain the code is fine, I'm just not adding something to the searchbox which enables the user to update the date range.

I should also mention that I know this won't make any difference to what the search results return yet. Getting that functionality working is my next challenge.

Thanks in advance!

*Edit: I forgot to mention that I didn't write all of this code. I've been tasked to learn how to add this little feature in as an introduction to programming as a whole.

**Edit: Can anybody show me a fix for this? Like I say I'm very new to this and I don't fully understand everything yet.

They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen. - see here

"props" is a channel to transport user's input into your component. eg

<DatePickerWidget startDate="2020/09/01" endDate="2020/09/30"/>

In your DatePickerWidget component, you can read the user's input "2020/09/01" by this.props.startDate . If you'd like to read this.props.update as a function from your DatePickerWidget, then you need to input a function when you use it. Just like

<DatePickerWidget startDate="2020/09/01" endDate="2020/09/30"/ update=<your_update_handler>>

I guess you want to pass the SearchBox's update function to DatePickerWidget, so you could do this

<DatePickerWidget startDate="2020/09/01" endDate="2020/09/30"/ update=this.props.update>

There is not function called update on the props object. Either you can define a function called update in the parent component of the components you want to use the this.props.update on. And then pass it down to the child components like so:


<SearchBox update={this.props.update}/>

But what you are probably looking for is to update the state of the SearchBox. state can be changed from within the component, whereas props cannot be changed directly, only by making a change in the parent component.

In SearchBar

import React from "react";

import { TextField, Button, Typography } from "@material-ui/core";

import "./SearchBox.css"
import DatePickerWidget from "./DatePickerWidget"

class SearchBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { searchTerm: "", results: "" };

    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.validateSearch = this.validateSearch.bind(this);
    this.clear = this.clear.bind(this);

  
  }

  componentDidMount() {
    this.setState({ searchTerm: this.props.searchTerm });
  }
  componentDidUpdate(prevProps) {
    if (prevProps.searchTerm !== this.props.searchTerm)
      this.setState({ searchTerm: this.props.searchTerm });
  }

  handleChange(event) {
    this.setState({ [event.target.name]: event.target.value });
  }

  handleSubmit(event) {
    event.preventDefault();
    this.props.update({ searchTerm: this.state.searchTerm });
    this.setState({ results: this.state.searchTerm });
  }

  validateSearch() {
    return this.state.searchTerm !== "";
  }

  clear() {
    this.setState({ searchTerm: "", results: "" });
    this.props.update({ searchTerm: "" });
  }

  render() {
    return (
      <React.Fragment>
        <form id="search-form" onSubmit={this.handleSubmit}>
          <Button
            style={{ width: "40%" }}
            variant="outlined"
            color="primary"
            onClick={this.clear}
            disabled={this.state.results === ""}
          >
            Clear
          </Button>
          <TextField
            style={{ flexBasis: "100%" }}
            variant="outlined"
            label="Search Term"
            type="search"
            name="searchTerm"
            value={this.state.searchTerm}
            onChange={this.handleChange}
          />
          <Button
            style={{ width: "40%" }}
            variant="contained"
            color="primary"
            disabled={!this.validateSearch()}
            type="submit"
          >
            Search
          </Button>


          <DatePickerWidget startDate="2020/09/01" endDate="2020/09/30" update={() => console.log('props passed')}/>
        </form>

        
        {this.state.results && (
          <Typography align="center" variant="body1">
            Displaying results for: {this.state.results}
          </Typography>
        )}
      </React.Fragment>
    );
  }
}


export default SearchBox;

And In Your date picker

import DateFnsUtils from "@date-io/date-fns";
import {
  MuiPickersUtilsProvider,
  KeyboardDatePicker,
} from "@material-ui/pickers";

class DatePickerWidget extends React.Component {
  render() {
    return (
      <Paper id="date-picker-widget" elevation={8}>
        <Paper id="date-filters" elevation={8}>
          <MuiPickersUtilsProvider utils={DateFnsUtils}>
            <KeyboardDatePicker
              margin="normal"
              id="date-picker-dialog"
              label="Start Date"
              format="yyyy/MM/dd"
              value={this.props.startDate}
              maxDate={this.props.endDate}
              onChange={(date) => this.props.update({ startDate: date })}
              KeyboardButtonProps={{
                "aria-label": "change start date",
              }}
            />
            <KeyboardDatePicker
              margin="normal"
              id="date-picker-dialog"
              label="End Date"
              format="yyyy/MM/dd"
              value={this.props.endDate}
              minDate={this.props.startDate}
              onChange={ this.props.update}
              KeyboardButtonProps={{
                "aria-label": "change end date",
              }}
            />
          </MuiPickersUtilsProvider>
        </Paper>
      </Paper>
    );
  }
}

export default DatePickerWidget;

This all was caused because you never passed any props to your DatePicker from parent

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM