【发布时间】:2021-05-05 17:08:49
【问题描述】:
我有一个简单的 React 应用程序,我将完全发布(它不会那么长)。该应用程序不起作用,但也不会引发任何错误。我试图记录状态,结果发现它们永远不会改变。我正在使用 大东西,例如自定义 hook 和 useReducer,但我怀疑我缺乏理解 react 工作原理的基本原理。
以下是该应用应如何工作的简短摘要:
有一个 Form 组件,它返回一系列自定义 Input 元素(这里只有两个)。Input 组件将验证逻辑外包给返回 [isTouched, isValid, dispatcherOfTheCustomHookReducer] 的自定义钩子。当事件发生时,Input 组件调用自定义钩子的调度程序,然后应根据自定义钩子中 reducer 返回的状态将样式应用于<input> 元素。
此外,由于 Form 组件需要知道整个表单是否有效,因此每个 Input 都有一个 onChangeValidity 属性,用于提升 isValid 状态。
理论上,表单在开始时应该是中性的,然后在您聚焦并模糊输入之后,它应该变为有效(蓝色背景)或无效(红色背景)。
我可能应该在提交后重置输入并添加其他内容,但现在我想让应用程序正常工作。目前状态永远不会改变,形式总是中性的(白色)。
您可能更喜欢查看codesandbox 中的文件。
App.js
import Form from './components/Form';
function App() {
return (
<div className="app">
<Form />
</div>
);
}
export default App;
Form.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;
Input.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;
}
【问题讨论】:
-
在代码沙箱上创建它。调试起来很容易
-
@Sword我没用过,我去试试
-
我做到了。这是codesanbox。我注意到与我在浏览器中得到的不同:在代码和框中,提交按钮始终应用
disabled样式,而在浏览器中从不应用。理论上它应该只在表单被触摸和无效时应用。 -
您的
if (!isTouched) isTouched = true;位于if (isTouched)内部,因此它永远不会到达那里。此外,您的输入 className 应如下所示: {classes[isTouched ?generic_input-input--${isValid ? "valid" : "invalid"}: "" ] } -
这是你的沙箱,
isTouched在更改时更改为true,并且 className 固定在你的输入中:codesandbox.io/s/bold-waterfall-vop71?file=/src/hooks/…
标签: javascript reactjs react-hooks state