为什么 onInput 不是函数?关于 useEffect() 的问题
Why is onInput not a function? A question about useEffect()
我对 React 和 hooks 还很陌生,我正在努力 useEffect()
。我已经观看了所有视频并阅读了所有文档,但仍然无法完全理解我遇到的错误。 ("onInput is not a function" 当我的新文章路线加载时)。 onInput 指向我的 form-hook.js
中的一个回调函数。为什么不注册?
在我的 input.js
组件中:
import React, { useReducer, useEffect } from 'react';
import { validate } from '../../util/validators';
import './Input.css';
const inputReducer = (state, action) => {
switch (action.type) {
case 'CHANGE':
return {
...state,
value: action.val,
isValid: validate(action.val, action.validators)
};
case 'TOUCH': {
return {
...state,
isTouched: true
}
}
default:
return state;
}
};
const Input = props => {
const [inputState, dispatch] = useReducer(inputReducer, {
value: props.initialValue || '',
isTouched: false,
isValid: props.initialValid || false
});
const { id, onInput } = props;
const { value, isValid } = inputState;
useEffect(() => {
console.log(id);
onInput(id, value, isValid)
}, [id, value, isValid, onInput]);
const changeHandler = event => {
dispatch({
type: 'CHANGE',
val: event.target.value,
validators: props.validators
});
};
const touchHandler = () => {
dispatch({
type: 'TOUCH'
});
};
//if statement to handle if you are updating an article and touch the category.... but it's valid
const element =
props.element === 'input' ? (
<input
id={props.id}
type={props.type}
placeholder={props.placeholder}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
/>
) : (
<textarea
id={props.id}
rows={props.rows || 3}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
/>
);
return (
<div
className={`form-control ${!inputState.isValid && inputState.isTouched &&
'form-control--invalid'}`}
>
<label htmlFor={props.id}>{props.label}</label>
{element}
{!inputState.isValid && inputState.isTouched && <p>{props.errorText}</p>}
</div>
);
};
export default Input;
useEffect(() => {onInput
指向 NewArticle.js
组件中的 onInput 属性,用户可以在其中输入新文章。
import Input from '../../shared/components/FormElements/Input';
import { useForm } from '../../shared/hooks/form-hook';
const NewArticle = () => {
const [formState, inputHandler] = useForm({
title: {
value: '',
isValid: false
}
}, false );
return (
<Input
id="title"
element="input"
type="text"
label="Title"
onInput={inputHandler}
/> );
};
export default NewArticle;
...然后在我的 form-hook.js
inputHandler 中是一个回调。所以,onInput 通过一个 prop 指向一个回调函数。它正在工作,将 onInput 注册为一个函数,然后突然抛出一个错误。给出了什么?
import { useCallback, useReducer } from 'react';
const formReducer = (state, action) => {
switch (action.type) {
case 'INPUT_CHANGE':
let formIsValid = true;
for (const inputId in state.inputs) {
if (!state.inputs[inputId]) {
continue;
}
if (inputId === action.inputId) {
formIsValid = formIsValid && action.isValid;
} else {
formIsValid = formIsValid && state.inputs[inputId].isValid;
}
}
return {
...state,
inputs: {
...state.inputs,
[action.inputId]: { value: action.value, isValid: action.isValid }
},
isValid: formIsValid
};
case 'SET_DATA':
return {
inputs: action.inputs,
isValid: action.formIsValid
};
default:
return state;
}
};
export const useForm = (initialInputs, initialFormValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialFormValidity
});
const inputHandler = useCallback((id, value, isValid) => {
dispatch({
type: 'INPUT_CHANGE',
value: value,
isValid: isValid,
inputId: id
});
}, []);
const setFormData = useCallback((inputData, formValidity) => {
dispatch({
type: 'SET_DATA',
inputs: inputData,
formIsValid: formValidity
});
}, []);
return [formState, inputHandler, setFormData];
};
谢谢你们。
我可以就如何重构代码给您一些建议。这将最终解决您的问题。
维护单一事实来源
- 您 UI 的当前状态应存储在一个位置。
- 如果状态由多个组件共享,您最好的选择是使用由 Context API (redux) 传递的 reducer,或者将容器组件的状态作为 props 传递给 Input 组件 (您当前的策略)。
- 这意味着您应该删除
Input
组件的 inputReducer
。
onInput
道具应该更新容器组件中的状态,然后将新的 inputValue
传递给 Input
组件。
- DOM
input
元素应该直接调用 onInput
而不是作为副作用。
- 删除
useEffect
调用。
关注点分离
动作应该与钩子分开定义。传统上,动作是一个函数,returns 一个传递给调度的对象。
我相当确定此处的 useCallback 调用对性能的损害大于帮助。例如 inputHandler
可以这样重组:
const inputChange = (inputId, value, isValid) => ({
type: 'INPUT_CHANGE',
value,
isValid,
inputId
})
export const useForm = (initialInputs, initialFormValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialFormValidity,
})
const inputHandler = (id, value, isValid) => dispatch(
inputChange(id, value, isValid)
)
}
- 了解如何在浏览器中使用
debugger
或断点。如果您在 useEffect
调用中放置一个断点,您将很快能够诊断出您的问题。
我对 React 和 hooks 还很陌生,我正在努力 useEffect()
。我已经观看了所有视频并阅读了所有文档,但仍然无法完全理解我遇到的错误。 ("onInput is not a function" 当我的新文章路线加载时)。 onInput 指向我的 form-hook.js
中的一个回调函数。为什么不注册?
在我的 input.js
组件中:
import React, { useReducer, useEffect } from 'react';
import { validate } from '../../util/validators';
import './Input.css';
const inputReducer = (state, action) => {
switch (action.type) {
case 'CHANGE':
return {
...state,
value: action.val,
isValid: validate(action.val, action.validators)
};
case 'TOUCH': {
return {
...state,
isTouched: true
}
}
default:
return state;
}
};
const Input = props => {
const [inputState, dispatch] = useReducer(inputReducer, {
value: props.initialValue || '',
isTouched: false,
isValid: props.initialValid || false
});
const { id, onInput } = props;
const { value, isValid } = inputState;
useEffect(() => {
console.log(id);
onInput(id, value, isValid)
}, [id, value, isValid, onInput]);
const changeHandler = event => {
dispatch({
type: 'CHANGE',
val: event.target.value,
validators: props.validators
});
};
const touchHandler = () => {
dispatch({
type: 'TOUCH'
});
};
//if statement to handle if you are updating an article and touch the category.... but it's valid
const element =
props.element === 'input' ? (
<input
id={props.id}
type={props.type}
placeholder={props.placeholder}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
/>
) : (
<textarea
id={props.id}
rows={props.rows || 3}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
/>
);
return (
<div
className={`form-control ${!inputState.isValid && inputState.isTouched &&
'form-control--invalid'}`}
>
<label htmlFor={props.id}>{props.label}</label>
{element}
{!inputState.isValid && inputState.isTouched && <p>{props.errorText}</p>}
</div>
);
};
export default Input;
useEffect(() => {onInput
指向 NewArticle.js
组件中的 onInput 属性,用户可以在其中输入新文章。
import Input from '../../shared/components/FormElements/Input';
import { useForm } from '../../shared/hooks/form-hook';
const NewArticle = () => {
const [formState, inputHandler] = useForm({
title: {
value: '',
isValid: false
}
}, false );
return (
<Input
id="title"
element="input"
type="text"
label="Title"
onInput={inputHandler}
/> );
};
export default NewArticle;
...然后在我的 form-hook.js
inputHandler 中是一个回调。所以,onInput 通过一个 prop 指向一个回调函数。它正在工作,将 onInput 注册为一个函数,然后突然抛出一个错误。给出了什么?
import { useCallback, useReducer } from 'react';
const formReducer = (state, action) => {
switch (action.type) {
case 'INPUT_CHANGE':
let formIsValid = true;
for (const inputId in state.inputs) {
if (!state.inputs[inputId]) {
continue;
}
if (inputId === action.inputId) {
formIsValid = formIsValid && action.isValid;
} else {
formIsValid = formIsValid && state.inputs[inputId].isValid;
}
}
return {
...state,
inputs: {
...state.inputs,
[action.inputId]: { value: action.value, isValid: action.isValid }
},
isValid: formIsValid
};
case 'SET_DATA':
return {
inputs: action.inputs,
isValid: action.formIsValid
};
default:
return state;
}
};
export const useForm = (initialInputs, initialFormValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialFormValidity
});
const inputHandler = useCallback((id, value, isValid) => {
dispatch({
type: 'INPUT_CHANGE',
value: value,
isValid: isValid,
inputId: id
});
}, []);
const setFormData = useCallback((inputData, formValidity) => {
dispatch({
type: 'SET_DATA',
inputs: inputData,
formIsValid: formValidity
});
}, []);
return [formState, inputHandler, setFormData];
};
谢谢你们。
我可以就如何重构代码给您一些建议。这将最终解决您的问题。
维护单一事实来源
- 您 UI 的当前状态应存储在一个位置。
- 如果状态由多个组件共享,您最好的选择是使用由 Context API (redux) 传递的 reducer,或者将容器组件的状态作为 props 传递给 Input 组件 (您当前的策略)。
- 这意味着您应该删除
Input
组件的inputReducer
。 onInput
道具应该更新容器组件中的状态,然后将新的inputValue
传递给Input
组件。- DOM
input
元素应该直接调用onInput
而不是作为副作用。 - 删除
useEffect
调用。
关注点分离
动作应该与钩子分开定义。传统上,动作是一个函数,returns 一个传递给调度的对象。
我相当确定此处的 useCallback 调用对性能的损害大于帮助。例如
inputHandler
可以这样重组:
const inputChange = (inputId, value, isValid) => ({
type: 'INPUT_CHANGE',
value,
isValid,
inputId
})
export const useForm = (initialInputs, initialFormValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialFormValidity,
})
const inputHandler = (id, value, isValid) => dispatch(
inputChange(id, value, isValid)
)
}
- 了解如何在浏览器中使用
debugger
或断点。如果您在useEffect
调用中放置一个断点,您将很快能够诊断出您的问题。