admin管理员组

文章数量:1406924

This is my form ponent:

Form.jsx

import React, { Component } from 'react';
import axios from 'axios';
import { Redirect } from 'react-router-dom';

class Form extends Component {
  constructor (props) {
    super(props);
    this.state = {
      formData: {
        restaurant: '',
        username: '',
        email: '',
        password: ''
      }
    };
    this.handleUserFormSubmit = this.handleUserFormSubmit.bind(this);
    this.handleFormChange = this.handleFormChange.bind(this);
  };
  ponentDidMount() {
    this.clearForm();
  };
  ponentWillReceiveProps(nextProps) {
    if (this.props.formType !== nextProps.formType) {
      this.clearForm();
    };
  };
  clearForm() {
    this.setState({
      formData: {restaurant: '', username: '', email: '', password: ''}
    });
  };
  handleFormChange(event) {
    const obj = this.state.formData;
    obj[event.target.name] = event.target.value;
    this.setState(obj);
  };
  handleUserFormSubmit(event) {
    event.preventDefault();
    const formType = this.props.formType
    const data = {
      restaurant: this.state.formData.restaurant,
      email: this.state.formData.email,
      password: this.state.formData.password
    };
    if (formType === 'register') {
      data.username = this.state.formData.username
    };
    const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/auth/${formType}`;
    axios.post(url, data)
    .then((res) => {
      this.clearForm();
      this.props.loginUser(res.data.auth_token);
    })
    .catch((err) => { console.log(err); });
  };
  render() {
    if (this.props.isAuthenticated) {
      return <Redirect to='/' />;
    };
  return (
    <div>
      {this.props.formType === 'Login' &&
        <h1 className="title is-1">Log In</h1>
      }
      {this.props.formType === 'Register' &&
        <h1 className="title is-1">Register</h1>
      }
      <hr/><br/>
      <form onSubmit={(event) => this.handleUserFormSubmit(event)}>
       {this.props.formType === 'Register' &&
        <div className="field">
         <input
          name="restaurant"
          className="input is-medium"
          type="text"
          placeholder="Enter your restaurant name"
          required
          value={this.state.formData.restaurant}
          onChange={this.props.handleFormChange}
        /> 
        </div>
        }
          <div className="field">
            <input
              name="username"
              className="input is-medium"
              type="text"
              placeholder="Enter a username"
              required
              value={this.state.formData.username}
              onChange={this.props.handleFormChange}
            />
          </div>
        <div className="field">
          <input
            name="email"
            className="input is-medium"
            type="email"
            placeholder="Enter an email address"
            required
            value={this.state.formData.email}
            onChange={this.props.handleFormChange}
          />
        </div>
        <div className="field">
          <input
            name="password"
            className="input is-medium"
            type="password"
            placeholder="Enter a password"
            required
            value={this.state.formData.password}
            onChange={this.props.handleFormChange}
          />
        </div>
        <input
          type="submit"
          className="button is-primary is-medium is-fullwidth"
          value="Submit"
        />
      </form>
    </div>
    )
  };
};

export default Form;

and this is my app ponent:

App.jsx

import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom';
import axios from 'axios';

import UsersList from './ponents/UsersList';
import About from './ponents/About';
import NavBar from './ponents/NavBar';
import Form from './ponents/Form';
import Logout from './ponents/Logout';
import UserStatus from './ponents/UserStatus';


class App extends Component {
  constructor() {
    super();
    this.state = {
      users: [],
      title: 'Test.io',
      isAuthenticated: false,
    };
    this.logoutUser = this.logoutUser.bind(this);
    this.loginUser = this.loginUser.bind(this);
  };
  ponentWillMount() {
    if (window.localStorage.getItem('authToken')) {
      this.setState({ isAuthenticated: true });
    };
  };
  ponentDidMount() {
    this.getUsers();
  };
  getUsers() {
    axios.get(`${process.env.REACT_APP_WEB_SERVICE_URL}/users`)
    .then((res) => { this.setState({ users: res.data.data.users }); })
    .catch((err) => { });
  };
  logoutUser() {
    window.localStorage.clear();
    this.setState({ isAuthenticated: false });
  };
  loginUser(token) {
    window.localStorage.setItem('authToken', token);
    this.setState({ isAuthenticated: true });
    this.getUsers();
  };
  render() {
    return (
      <div>
        <NavBar
          title={this.state.title}
          isAuthenticated={this.state.isAuthenticated}
        />
        <section className="section">
          <div className="container">
            <div className="columns">
              <div className="column is-half">
                <br/>
                <Switch>
                  <Route exact path='/' render={() => (
                    <UsersList
                      users={this.state.users}
                    />
                  )} />
                  <Route exact path='/about' ponent={About}/>
                  <Route exact path='/register' render={() => (
                    <Form
                      formType={'Register'}
                      isAuthenticated={this.state.isAuthenticated}
                      loginUser={this.loginUser}
                    />
                  )} />
                  <Route exact path='/login' render={() => (
                    <Form
                      formType={'Login'}
                      isAuthenticated={this.state.isAuthenticated}
                      loginUser={this.loginUser}
                    />
                  )} />
                  <Route exact path='/logout' render={() => (
                    <Logout
                      logoutUser={this.logoutUser}
                      isAuthenticated={this.state.isAuthenticated}
                    />
                  )} />
                  <Route exact path='/status' render={() => (
                    <UserStatus
                      isAuthenticated={this.state.isAuthenticated}
                    />
                  )} />
                </Switch>
              </div>
            </div>
          </div>
        </section>
      </div>
    )
  }
};

export default App;

This is the error console is showing:

index.js:1446 Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
    in input (at Form.jsx:72)
    in div (at Form.jsx:71)
    in form (at Form.jsx:69)
    in div (at Form.jsx:61)
    in Form (at App.jsx:66)
    in Route (at App.jsx:65)
    in Switch (at App.jsx:58)
    in div (at App.jsx:56)
    in div (at App.jsx:55)
    in div (at App.jsx:54)
    in section (at App.jsx:53)
    in div (at App.jsx:48)
    in App (at src/index.js:9)
    in Router (created by BrowserRouter)
    in BrowserRouter (at src/index.js:8)

I don't get it, though, because form change is being handled at <input> in the code above, like so:

onChange={this.props.handleFormChange}

so what am I missing? forms are not even accepting inputs.

This is my form ponent:

Form.jsx

import React, { Component } from 'react';
import axios from 'axios';
import { Redirect } from 'react-router-dom';

class Form extends Component {
  constructor (props) {
    super(props);
    this.state = {
      formData: {
        restaurant: '',
        username: '',
        email: '',
        password: ''
      }
    };
    this.handleUserFormSubmit = this.handleUserFormSubmit.bind(this);
    this.handleFormChange = this.handleFormChange.bind(this);
  };
  ponentDidMount() {
    this.clearForm();
  };
  ponentWillReceiveProps(nextProps) {
    if (this.props.formType !== nextProps.formType) {
      this.clearForm();
    };
  };
  clearForm() {
    this.setState({
      formData: {restaurant: '', username: '', email: '', password: ''}
    });
  };
  handleFormChange(event) {
    const obj = this.state.formData;
    obj[event.target.name] = event.target.value;
    this.setState(obj);
  };
  handleUserFormSubmit(event) {
    event.preventDefault();
    const formType = this.props.formType
    const data = {
      restaurant: this.state.formData.restaurant,
      email: this.state.formData.email,
      password: this.state.formData.password
    };
    if (formType === 'register') {
      data.username = this.state.formData.username
    };
    const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/auth/${formType}`;
    axios.post(url, data)
    .then((res) => {
      this.clearForm();
      this.props.loginUser(res.data.auth_token);
    })
    .catch((err) => { console.log(err); });
  };
  render() {
    if (this.props.isAuthenticated) {
      return <Redirect to='/' />;
    };
  return (
    <div>
      {this.props.formType === 'Login' &&
        <h1 className="title is-1">Log In</h1>
      }
      {this.props.formType === 'Register' &&
        <h1 className="title is-1">Register</h1>
      }
      <hr/><br/>
      <form onSubmit={(event) => this.handleUserFormSubmit(event)}>
       {this.props.formType === 'Register' &&
        <div className="field">
         <input
          name="restaurant"
          className="input is-medium"
          type="text"
          placeholder="Enter your restaurant name"
          required
          value={this.state.formData.restaurant}
          onChange={this.props.handleFormChange}
        /> 
        </div>
        }
          <div className="field">
            <input
              name="username"
              className="input is-medium"
              type="text"
              placeholder="Enter a username"
              required
              value={this.state.formData.username}
              onChange={this.props.handleFormChange}
            />
          </div>
        <div className="field">
          <input
            name="email"
            className="input is-medium"
            type="email"
            placeholder="Enter an email address"
            required
            value={this.state.formData.email}
            onChange={this.props.handleFormChange}
          />
        </div>
        <div className="field">
          <input
            name="password"
            className="input is-medium"
            type="password"
            placeholder="Enter a password"
            required
            value={this.state.formData.password}
            onChange={this.props.handleFormChange}
          />
        </div>
        <input
          type="submit"
          className="button is-primary is-medium is-fullwidth"
          value="Submit"
        />
      </form>
    </div>
    )
  };
};

export default Form;

and this is my app ponent:

App.jsx

import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom';
import axios from 'axios';

import UsersList from './ponents/UsersList';
import About from './ponents/About';
import NavBar from './ponents/NavBar';
import Form from './ponents/Form';
import Logout from './ponents/Logout';
import UserStatus from './ponents/UserStatus';


class App extends Component {
  constructor() {
    super();
    this.state = {
      users: [],
      title: 'Test.io',
      isAuthenticated: false,
    };
    this.logoutUser = this.logoutUser.bind(this);
    this.loginUser = this.loginUser.bind(this);
  };
  ponentWillMount() {
    if (window.localStorage.getItem('authToken')) {
      this.setState({ isAuthenticated: true });
    };
  };
  ponentDidMount() {
    this.getUsers();
  };
  getUsers() {
    axios.get(`${process.env.REACT_APP_WEB_SERVICE_URL}/users`)
    .then((res) => { this.setState({ users: res.data.data.users }); })
    .catch((err) => { });
  };
  logoutUser() {
    window.localStorage.clear();
    this.setState({ isAuthenticated: false });
  };
  loginUser(token) {
    window.localStorage.setItem('authToken', token);
    this.setState({ isAuthenticated: true });
    this.getUsers();
  };
  render() {
    return (
      <div>
        <NavBar
          title={this.state.title}
          isAuthenticated={this.state.isAuthenticated}
        />
        <section className="section">
          <div className="container">
            <div className="columns">
              <div className="column is-half">
                <br/>
                <Switch>
                  <Route exact path='/' render={() => (
                    <UsersList
                      users={this.state.users}
                    />
                  )} />
                  <Route exact path='/about' ponent={About}/>
                  <Route exact path='/register' render={() => (
                    <Form
                      formType={'Register'}
                      isAuthenticated={this.state.isAuthenticated}
                      loginUser={this.loginUser}
                    />
                  )} />
                  <Route exact path='/login' render={() => (
                    <Form
                      formType={'Login'}
                      isAuthenticated={this.state.isAuthenticated}
                      loginUser={this.loginUser}
                    />
                  )} />
                  <Route exact path='/logout' render={() => (
                    <Logout
                      logoutUser={this.logoutUser}
                      isAuthenticated={this.state.isAuthenticated}
                    />
                  )} />
                  <Route exact path='/status' render={() => (
                    <UserStatus
                      isAuthenticated={this.state.isAuthenticated}
                    />
                  )} />
                </Switch>
              </div>
            </div>
          </div>
        </section>
      </div>
    )
  }
};

export default App;

This is the error console is showing:

index.js:1446 Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
    in input (at Form.jsx:72)
    in div (at Form.jsx:71)
    in form (at Form.jsx:69)
    in div (at Form.jsx:61)
    in Form (at App.jsx:66)
    in Route (at App.jsx:65)
    in Switch (at App.jsx:58)
    in div (at App.jsx:56)
    in div (at App.jsx:55)
    in div (at App.jsx:54)
    in section (at App.jsx:53)
    in div (at App.jsx:48)
    in App (at src/index.js:9)
    in Router (created by BrowserRouter)
    in BrowserRouter (at src/index.js:8)

I don't get it, though, because form change is being handled at <input> in the code above, like so:

onChange={this.props.handleFormChange}

so what am I missing? forms are not even accepting inputs.

Share Improve this question asked Mar 10, 2019 at 4:39 8-Bit Borges8-Bit Borges 10.1k30 gold badges110 silver badges212 bronze badges 8
  • That's because the handleFormChange is not a prop; it's located in Form.jsx – jstarnate Commented Mar 10, 2019 at 5:07
  • It should be: onChange={this.handleFormChange} – jstarnate Commented Mar 10, 2019 at 5:08
  • so should all props be removed from form.jsx? – 8-Bit Borges Commented Mar 10, 2019 at 5:13
  • Check your code. If a method is located in Form.jsx, it's not a prop, it's a local method. – jstarnate Commented Mar 10, 2019 at 5:16
  • Because a prop is a way of a child ponent to get data from the parent ponent – jstarnate Commented Mar 10, 2019 at 5:18
 |  Show 3 more ments

2 Answers 2

Reset to default 2

You have fundamentally misunderstood the props concept in React ponents. I will try to explain it with a more simplified version of your app. Lets take the form example.

class Form extends Component {

  handleFormChange(){
    console.log("This is the form change function inside -Form-");
  }

  render(){
    return(
    <div>
     <input
      name="email"
      type="text"
      value={this.state.email}
      onChange={this.handleFormChange} // Focus point 1 - Calls local function
    />
     <input
      name="username"
      type="text"
      value={this.state.username}
      onChange={this.props.handleFormChange} // Focus point 2 - Calls function passed down via props
    />  
   </div>

    );
  }


}


class App extends Component {

  handleFormChange(){
      console.log("This is the form change function inside -App-");
  }

  render(){
    return <Form handleFormChange={this.handleFormChange} />
  }

}

As you can see the App is going to render the Form ponent. Look at Focus point 1 and 2. In the first focus point its trying to access the local 'handleFormChange' function. And the 2nd one tries to call whatever the function that is provided by the parent via props.

So what happened is that you are telling the 'Form' ponent to access the handleFormChange function which should have been provided by the parent as a "prop" i.e this.props.handleFormChange. So when the ponent is mounted React tries to bind this.props.handleFormChange to the onChange event of the input.

But in your instance, the 'handleFormChange' prop in the ponent is not provided. hence this.props.handleFormChange will be undefined resulting in that warning.

So to wire up any handlers that are within the Form ponent they should not be linked with 'this.props'. Whatever handlers that are accessed via props should be provided by the parent when initializing the ponent.

Its because you are not passing any prop named as handleFormChange from App.jsx to the Form ponent.

Instead, it's in your own Form ponent.

So, just try this onChange={this.handleFormChange}

本文标签: javascriptReact Failed prop type value without onChange handlerStack Overflow