【问题标题】:Child component sharing state with parent?子组件与父组件共享状态?
【发布时间】:2019-02-12 02:38:25
【问题描述】:

我对 React 还很陌生,我的第一个任务有点吃力。

我正在创建一个通用 TextBox(来自 MaterialUI)组件,并创建了一个扩展 TextBox 的子组件 (NumberInput)。问题是,每当我在 NumberInput 中输入任何输入时,NumberInput 状态似乎都没有更新。任何提示将不胜感激!

家长:

import React, { Component } from 'react';
import TextField from '@material-ui/core/TextField';
import { Validations } from './validations'
import PropTypes from 'prop-types';

class TextBox extends Component {
    constructor(props) {
        super(props);

        this.state = {
            value: null,
            error: false,
            errorMsg: null
        };

    this.setValue = this.setValue.bind(this);
    this.setError = this.setError.bind(this);
    this.setErrorMsg = this.setErrorMsg.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.checkForErrors = this.checkForErrors.bind(this);

}

// Handles changes upon each edit of the textbox. Updates the state value and checks for errors.
handleChange(event) {
    this.setValue(event);
}

setValue(event) {
    var value = document.getElementById(this.props.id).value;
    this.setState({ value: value }, () => this.checkForErrors());
}

setError(error, errorMsg) {
    this.setState({ error: error });
    this.setErrorMsg(errorMsg);
}

setErrorMsg(errorMsg) { this.setState({ errorMsg: errorMsg }) }

getValue() { return this.state.value }

getError() { return this.state.error }

getErrorMsg() { return this.state.errorMsg }

// Checks for input error
checkForErrors() {
    var input = this.getValue();
    console.log(input);
    var validations = Validations(this.props, input);
    this.setError(validations.error, validations.errorMsg);
}

render() {
    let {
        label,
        placeholder,
        maxLength,
        minLength,
        maxNumber,
        minNumber,
        disabled,
        required,
        type,
        multiline,
        id,
        name,
        allowSpecialCharacters,
        allowWhitespace
    } = this.props;

    return (
        <div>
            <br/>
            <TextField
                error = { this.getError() }
                label = { label }
                helperText = { this.getErrorMsg() }
                placeholder = { placeholder }
                inputProps = {{ maxLength: maxLength }}
                disabled = { disabled }
                required = { required }
                type = { type }
                multiline = { multiline }
                onChange = { this.handleChange }
                id = { id }
                name = { name }
            />
        </div>
    );
 }
};

TextBox.propTypes = {
    label: PropTypes.string,
    helperText: PropTypes.string,
    placeholder: PropTypes.string,
    maxLength: PropTypes.number,
    minLength: PropTypes.number,
    maxNumber: PropTypes.number,
    minNumber: PropTypes.number,
    disabled: PropTypes.bool,
    required: PropTypes.bool,
    type: PropTypes.oneOf(["button", "checkbox", "color", "date", "datetime-local", "email", "file",
    "hidden", "image", "month", "number", "password", "radio", "range", "reset", "search",
        "submit", "tel", "text", "time", "url", "week"]),
    multiline: PropTypes.bool,
    id: PropTypes.string,
    name: PropTypes.string,
    allowSpecialCharacters: PropTypes.bool,
    allowWhitespace: PropTypes.bool
}

TextBox.defaultProps = {
    label: '',
    helperText: '',
    placeholder: '',
    maxLength: 100000,
    minLength: 0,
    maxNumber: 100000,
    minNumber: -100000,
    disabled: false,
    required: false,
    type: 'text',
    multiline: false,
    id: 'input',
    name: '',
    allowSpecialCharacters: true,
    allowWhitespace: true   
}

export default TextBox;

孩子:

import React, { Component } from 'react';
import TextBox from './textbox';
import { Validations } from './validations'
import PropTypes from 'prop-types';

// const NumberInput = props => {
//  return (
//      <TextBox {...props} type="number" />
//  )
// }

class NumberInput extends TextBox {

    constructor(props) {
        super(props);

        this.state = {
            value: null,
            error: false,
            errorMsg: null
        };  
    }

    render() {

        return (
            <div>
                <TextBox
                    label = { "NumberInput" }
                    type = { "number" }             
                />
            </div>
        )

    }
};

export default NumberInput;

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import TextBox from './widgets/textbox/textbox';
import TextArea from './widgets/textbox/textarea';
import NumberInput from './widgets/textbox/numberinput';

class App extends Component { 
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to 
  reload.
        </p>
        <TextBox type="text" label={"Textbox"} />
        <NumberInput label={"NumberInput"} />
      </div>
    );
  }
}

export default App;

更新NumberInput:

const NumberInput = props => {
    return (
        <TextBox {...props} type="number" id="inp" />
    )
}

【问题讨论】:

    标签: reactjs components extend


    【解决方案1】:

    您不需要在这里为您的子组件使用一个类并从父组件扩展它。当您将处理函数传递给子组件时,此过程非常简单。

    class Parent extends React.Component {
      state = {
        value: "initial value",
      }
    
      handleChange = e => this.setState({ value: e.target.value });
    
      render() {
        return (
          <div>
            <p>Change the value:</p>
            <Child onChange={this.handleChange} value={this.state.value} />
            <p>{JSON.stringify(this.state.value)}</p>
          </div>
        );
      }
    }
    
    const Child = props => {
      return (
        <input onChange={props.onChange} value={props.value} />
      )
    }
    
    ReactDOM.render(<Parent />, document.getElementById("root"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>

    【讨论】:

    • 我在App.js 中尝试做的是调用组件NumberInputNumberInput 扩展自另一个名为 TextBox 的组件。在我的TextBox 组件中,我进行了所有验证并返回输入是否有效。但是,它一直失败,因为传递给验证函数的值是空的。我是否以错误的方式处理这件事?我应该在NumberInput 内进行所有验证吗?如果我想创建另一个组件,比如UrlInput,我是否必须在UrlInput 中再次重做所有验证?
    • 我不能完全遵循您的代码,但您不需要从另一个组件扩展组件。我想这不是 React 方式。您可以创建可重用的组件(而不是从另一个组件扩展),并且可以在多个地方以多种方式使用它们。但是,如果您在此组件中保留另一个状态,则很难将此状态传递给您的父组件。您正在尝试在这里创建一个带有错误检查的简单表单组件。也许最好检查一下目前的软件包,这些软件包正是这样做的。
    • 谢谢!我不再尝试扩展组件,这有助于解决我的一个问题。更新后的NumberInput 现在在上面 :-)
    • 如果我希望 prop id 每次都具有唯一值,我应该定义一个单独的函数吗?我目前遇到了这个问题,我的 defaultProps 设置为 id: 'input'
    • 不客气。我是否理解正确,您需要一个唯一的 ID?如果是这样,您可以使用uiid 生成器。
    猜你喜欢
    • 2016-01-25
    • 2017-11-23
    • 1970-01-01
    • 2019-05-31
    • 1970-01-01
    • 2017-07-10
    • 2018-10-05
    • 2020-10-05
    • 1970-01-01
    相关资源
    最近更新 更多