javascript - React JS 表单验证逻辑位于子级还是父级?

标签 javascript reactjs react-native

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

在我的示例代码中,有一个用于输入框的小子组件,一个包含两个输入框的中间组件(我们将其称为用户名控件),然后顶部组件是一个编辑页面。每个组件都有自己的 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')
);

最佳答案

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

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')
);

关于javascript - React JS 表单验证逻辑位于子级还是父级?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61642478/

相关文章:

ios - 未找到 RCTBridgeModule - RNGoogleSignin

javascript - 按钮 - 启用和禁用

javascript - 使用 xml2js 构建具有相等子键的 xml?

javascript - 如何停止 react native 动画

javascript - react native : state is getting undefined inside useCallBack hook function

javascript - 如何在 native react 中从数组中删除重复项

css - 生成的 css 文件应该与我的组件的源代码放在一起吗?

javascript - 使用 AngularJS 删除和更新 json 对象

javascript - 如何在流程中键入检查日期对象?

javascript - 在 window.onbeforeunload 事件中使用 window.event.keyCode 在 javascript 中捕获 f5 按键事件始终为 0 而不是 116