【问题标题】:React JS form validation logic located in child or parent?React JS 表单验证逻辑位于子级还是父级?
【发布时间】:2020-05-06 18:22:28
【问题描述】:

当涉及到简单的验证逻辑之类的事情时,我不确定我是否了解如何组织我的第一个 React JS 应用程序。我的想法是将尽可能多的逻辑放入每个子组件中,但在我看来,当lifting up state 更合乎逻辑的地方是父组件时。但这对我来说似乎是错误的,因为父母最终可能会为所有孩子提供大量代码。我提供了一个非常简单的例子来说明这个问题。

在我的示例代码中,有一个用于输入框的小型子组件,一个包含其中两个输入框的中间组件(我们称之为用户名控件),然后顶部组件是一个编辑页面。每个组件都有自己的isValid 版本。最低的孩子在有效之前验证输入不是空的。中间组件验证两个输入框都有内容。顶部父编辑页面的作用与中间组件相同。我的问题是存储这个简单验证逻辑的“理想”位置或级别是什么?

考虑到顶层编辑页面有一个简单的按钮,只有在检查所有验证时才应该启用该按钮。如果您使用各种规则管理表单上的许多部分,这让我感到困惑。因此,不仅用户名部分需要有效,其他几个部分也需要有效。

我的主要困惑是我认为我遗漏了一些简单的东西,因为当涉及到顶部组件(编辑页面)上的按钮时,我认为中间组件(用户控件)不能传达它的验证状态,除非我使用一个参考。在阅读了一些参考资料后,似乎它们并不是为了过度使用而设计的。如果编辑页面无法从子用户名控件获得关于验证的反馈,则父需要进行验证。在大型表单的情况下,这意味着在父编辑页面中存储了大量的验证逻辑。这可能是正常的预期,我只是不确定。

任何人都可以验证在我概述的场景中放置简单isValid 检查的“适当”位置吗?

class InputBox extends React.Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
  }
  handleInputChange(e) {
    this.props.onChange(e);
  }
  render() {
    const inputValue = this.props.value;
    const inputName = this.props.name;
    const isValid = this.props.isValid;
    const label = this.props.label;
    const msg = isValid === true ? 'true' : 'false';
    return (
      <div>
        <span>{label}</span>
        <input type="text"
          name={inputName}
          value={inputValue}
          onChange={this.handleInputChange} />
        <span>Is this textbox valid = {msg}</span>
      </div>
    )}
}
class UserNameControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }
  handleChange(e) {
    const controlName = e.target.name;
    const newValue = e.target.value;
    const inputValue1 = controlName === 'input1' ? newValue : this.props.value1;
    const inputValue2 = controlName === 'input2' ? newValue : this.props.value2;
    const isValid = inputValue1 === '' || inputValue2 === '' ? false : true;
    this.props.onChange(controlName,newValue,isValid);
  }
  render() {
    const inputValue1 = this.props.value1;
    const inputValue2 = this.props.value2;
    const isTextValid1 = inputValue1 === '' ? false : true;
    const isTextValid2 = inputValue2 === '' ? false : true;
    const msg1 = isTextValid1 === true ? 'true' : 'false';
    const msg2 = isTextValid2 === true ? 'true' : 'false';
    const isSectionValidMsg = inputValue1 === '' || inputValue2 === '' ? 'false' : 'true';
    const isValid = inputValue1 === '' || inputValue2 === '' ? false : true;
    //this.props.isValid(isValid);
    return (
      <div>
        <div><h2>User Name Control Header</h2></div>
        <InputBox
          label='First Name: '
          name='input1'
          value={inputValue1}
          isValid={isTextValid1}
          onChange={this.handleChange}/>
          <br/>
        <InputBox
          label='Last Name: '
          name='input2'
          value={inputValue2}
          isValid={isTextValid2}
          onChange={this.handleChange}/>
        <div>
           <h4>User Name Control Footer: Is section valid = {isSectionValidMsg}</h4>
        </div>
       </div>
    )}
}
class EditPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      pageIsValid: false,
      textValue1: '',
      textValue2: '',
      section1Valid: false
    };
    this.handleChange = this.handleChange.bind(this);
    this.updateSection1Status = this.updateSection1Status.bind(this);
  }
  updateSection1Status(boolValue) {
    this.setState({section1Valid: boolValue});
  }
  handleChange(controlName,inputValue,isSectionValid) {
    this.setState({section1Valid: isSectionValid});
    if(controlName==='input1') {
      this.setState({textValue1: inputValue});
    }
    if(controlName==='input2') {
      this.setState({textValue2: inputValue});
    }
  }

  render() {
    const isFormValid = this.state.textValue1 === '' || this.state.textValue2 === '' ? false : true;
    //const formValidMsg = isFormValid ? 'True' : 'False';
    const formValidMsg = this.state.section1Valid ? 'True' : 'False';
    return (
      <form>
      <h1>Edit Form</h1>
        <div>
          <UserNameControl
            value1={this.state.textValue1}
            value2={this.state.textValue2}
            onChange={this.handleChange}
            />
        </div>
        <div>
          <h4>Is form valid = {formValidMsg}</h4>
        </div>
      </form>
    );
  }
}

ReactDOM.render(
  <EditPage/>,
  document.getElementById('root')
);

【问题讨论】:

    标签: javascript reactjs react-native


    【解决方案1】:

    我认为您走在正确的道路上,父级应该处理大部分验证,因为这允许子级输入变得更加通用,因此在整个应用程序中更可重用。在这个特定场景中,我认为子组件应该尽可能通用,中间组件应该处理大部分验证逻辑,并将结果传递给父组件。然而,在这种情况下,您可能还需要在父级别进行验证,但老实说,整个事情可能会被重构为仅两个组件而不是三个组件。以下是我将如何处理您的场景:

    import React from 'react'
    
    const notEmptyValidation = value => Boolean(value)
    
    
    const InputBox = props => {
        const {validation, label, name} = props;
    
        const [localValue, setValue] = React.useState(props.value||"");
    
        const onChange = e => {
           setValue(e.target.value);
           if(typeof props.onChange === "function"){
             return props.onChange(e) 
          }
         }
    
        const isValid = () => {
          if(typeof validation === "function"){
             return validation(localValue) 
          }
          return true
        }
    
        React.useEffect(() => {
          //this allows the component to be used as either a controlled or uncontrolled input
          setValue(props.value||"")
        }, [props.value])
    
        return (
          <div>
            <span>{label}</span>
            <input type="text"
              name={name}
              value={localValue}
              onChange={onChange} />
            <span>Is this textbox valid = {`${isValid() ? "true":"false"}`}</span>
          </div>
        )
    }
    
    const UserNameControl = props => {
    
      const [input, setInput] = React.useState({
       input1: "",
       input2: ""
     })
    
      const onChange = e => {
       let localInput = {...input}; //need to create a new reference to input state
       localInput[e.target.name] = e.target.value
       setInput(localInput)
       if(typeof props.onChange === "function"){ //run the onChange callback from parent
             return props.onChange(localInput) 
         }
      }
    
      //validates that all values in the input state are populated
      const isValid = () => Object.keys(input).every(key => notEmptyValidation(input[key]))
    
      return (
          <div>
            <div><h2>User Name Control Header</h2></div>
            <InputBox
              label='First Name: '
              name='input1'
              value={input.inputValue1}
              validation={notEmptyValidation} //pass validation function down to the child
              onChange={onChange}/>
              <br/>
            <InputBox
              label='Last Name: '
              name='input2'
              value={input.inputValue2}
              validation={notEmptyValidation} //pass validation function down to the child
              onChange={onChange}/>
            <div>
               <h4>User Name Control Footer: Is section valid = {`${isValid() ? "true":"false"}`}.</h4>
            </div>
           </div>
        )
    
    }
    
    
    const EditPage = () => {
      const [userNameData, setUserNameData] = React.useState(null)
    
      const onChange = inputObj => setUserNameData(inputObj)
    
      //validates that the input was successful, and all entries are populated
      const isValid = () => userNameData && Object.keys(userNameData).every(key => notEmptyValidation(userNameData[key]))
    
    
      return (
          <form>
          <h1>Edit Form</h1>
            <div>
              <UserNameControl onChange={onChange}/>
            </div>
            <div>
              <h4>Is form valid = {`${isValid() ? "true":"false"}`}</h4>
            </div>
          </form>
        );
    }
    
    ReactDOM.render(
      <EditPage/>,
      document.getElementById('root')
    );
    
    

    【讨论】:

    • 感谢您的样品。由于某种原因,我无法让它在 Codeply 中工作,所以我需要尝试使用我的笔记本电脑并仔细查看。
    • 你是对的@pretzelb,感谢您指出这一点,我刚刚更新了上面的示例。这是该示例的有效jsfiddle。 jsfiddle.net/eharris91/273dsbeL/5
    • 我实际上认为这是 CodePly 的问题,因为 Jsfiddle 似乎对您的示例没有问题。关于我的问题,我首先尝试破译您的代码,因为它使用了我尚未遇到的方法和语法。一旦我这样做了,我希望我可以计划它在我正在构建的应用程序中的外观。我想问一个问题,但我需要先更好地了解这段代码在做什么以及如何扩展它。
    • 是的,看起来 codeply 不支持 >ES6 语法
    • 我终于有几分钟的时间来查看代码,我想我理解它是如何工作的,我现在只需要尝试将它应用到我的应用程序的场景中。我认为它不会改变我的应用程序尝试编辑具有许多复杂规则的巨大 XML 文件的任何内容,但我只需要一点时间来计划一些使用这种对我来说是新语法的场景。在这一点上,我的猜测是您的回答证实了我的怀疑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    相关资源
    最近更新 更多