繁体   English   中英

React.js 状态不会改变

[英]React.js states won't change

我有一个简单的 React 应用程序,我将完全发布它(它不会那么长)。 该应用程序不起作用,但也不会引发任何错误。 我试图记录状态,结果发现它们永远不会改变。 我正在使用自定义钩子和 useReducer 之类的大东西,但我怀疑我缺乏理解反应如何工作的基本原理。

以下是该应用程序应如何工作的简短摘要:
有一个Form组件,它返回一系列自定义Input元素(这里只有两个)。
Input组件将验证逻辑外包给返回[isTouched, isValid, dispatcherOfTheCustomHookReducer]的自定义钩子。 当事件发生时, Input组件调用自定义 hook 的 dispatcher,然后根据自定义 hook 中 reducer 返回的 state 将 styles 应用于<input>元素。
此外,由于 Form 组件需要知道整个表单是否有效,因此每个 Input 都有一个onChangeValidity属性,用于提升isValid state。 理论上,表格在开始时应该是中性的,然后,在您聚焦并模糊输入之后,它应该变为有效(蓝色背景)或无效(红色背景)。
我可能应该在提交后重置输入并添加其他内容,但现在我想让应用程序正常工作。 目前状态永远不会改变,forms 始终显示为中性(白色)。

您可能更喜欢查看代码和框中的文件。

应用程序.js

import Form from './components/Form';

function App() {
  return (
    <div className="app">
      <Form />
    </div>
  );
}

export default App;

表单.js

import { useReducer } from 'react';

import Input from './Input';

// put your inputs' ID here to generate the default state
const defaultState = (inputs = ['username', 'email']) => {
  let inputsState = {};
  for (const input of inputs) inputsState[input] = false;
  return { ...inputsState, isValid: false };
};

const formReducer = (state, action) => {

  let newInputsStateList = {...state, [action.id]: action.isValid};
  delete newInputsStateList.isValid;

  let isValid = true;
  for(const key in newInputsStateList) {
    if(!newInputsStateList[key]) isValid = false;
    break;
  }

  return { ...newInputsStateList, isValid};
}

const Form = props => {

  const [formState, dispatchFormState] = useReducer(formReducer, undefined, defaultState);

  const submitHandler = event => {
    event.preventDefault();
    console.log('You are logged in.');
  }

  return <form onSubmit={submitHandler}>

    <Input
      id='username'
      label='Username'
      type='text'
      test={username => username.trim().length > 6}
      onChangeValidity={validity => dispatchFormState({id: 'username', isValid: validity})}
    />

    <Input
      id='email'
      label='Email'
      type='email'
      test={email => email.includes('@')}
      onChangeValidity={validity => dispatchFormState({id: 'email', isValid: validity})}
    />

    <button type='submit' disabled={!formState.isValid} >Submit</button>
  </form>
};

export default Form;

输入.js

import { useEffect } from 'react';

import classes from './Input.module.css';
import useValidation from '../hooks/use-validation';

const Input = props => {

  const [isTouched, isValid, checkValidity] = useValidation();

  // eslint-disable-next-line
  useEffect(() => props.onChangeValidity(isValid), [isValid]);

  return <div className={classes.generic_input}>
    <label className={classes['generic_input-label']} htmlFor={props.id} >{props.label}</label>
    <input
      className={classes[`${isTouched ? 'generic_input-input--'+isValid ? 'valid' : 'invalid' : ''}`]}
      type={props.type}
      name={props.id}
      id={props.id}
      onChange={event => checkValidity({
        type: 'CHANGE',
        value: event.target.value,
        test: props.test
      })}
      onBlur={event => checkValidity({
        type: 'BLUR',
        value: event.target.value,
        test: props.test
      })}
    />
  </div>
};

export default Input;

使用验证.js

import { useReducer } from 'react';

const validationReducer = (state, action) => {

  let isTouched = state.isTouched;
  let isValid = state.isValid;

  if(action.type === 'CHANGE') if (isTouched) isValid = action.test(action.value);

  else if(action.type === 'BLUR') {

    isValid = action.test(action.value);

    if (!isTouched) isTouched = true;
  }

  else isTouched = isValid = false;

  return {isTouched, isValid};
}

const useValidation = () => {

  const [validationState, dispatchValidation] = useReducer(validationReducer, {isTouched: false, isValid: false});

  return [validationState.isTouched, validationState.isValid, dispatchValidation];
};

export default useValidation;

Input.module.css

.generic_input {
  display: flex;
  flex-direction: column;
  padding: 1rem;
}

.generic_input-label {
  font-weight: bold;
}

.generic_input-input--valid {
  background-color: lightblue;
}

.generic_input-input--invalid {
  border-color: red;
  background-color: rgb(250, 195, 187);
}

.submit:disabled {
  background-color: #CCC;
  color: #292929;
  border-color: #CCC;
  cursor: not-allowed;
}

我认为您需要在您的validationReducer中修复isTouched逻辑。 isTouched永远不会设置为true

就像是:

const validationReducer = (state, action) => {
  let isTouched = state.isTouched;
  let isValid = state.isValid;

  if (action.type === "CHANGE") {
    isTouched = true;
    isValid = action.test(action.value)
  } else if (action.type === "BLUR") {
    isValid = action.test(action.value);
  } else {
    isTouched = isValid = false;
  }

  return { isTouched, isValid };
};

...尽管我不确定您何时希望将isTouched再次设置为 false,因此逻辑需要一些工作...

此外,您输入的 class 不正确。

它应该看起来像:

  <input
     className={
       classes[
         isTouched
           ? `generic_input-input--${isValid ? "valid" : "invalid"}`
           : ""
       ]
     }
     ...
   >

看看这个沙盒

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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