为什么 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];
};

谢谢你们。

我可以就如何重构代码给您一些建议。这将最终解决您的问题。

  1. 维护单一事实来源

    • 您 UI 的当前状态应存储在一个位置。
    • 如果状态由多个组件共享,您最好的选择是使用由 Context API (redux) 传递的 reducer,或者将容器组件的状态作为 props 传递给 Input 组件 (您当前的策略)。
    • 这意味着您应该删除 Input 组件的 inputReducer
    • onInput 道具应该更新容器组件中的状态,然后将新的 inputValue 传递给 Input 组件。
    • DOM input 元素应该直接调用 onInput 而不是作为副作用。
    • 删除 useEffect 调用。
  2. 关注点分离

    • 动作应该与钩子分开定义。传统上,动作是一个函数,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)
                                                )
                         }
  1. 了解如何在浏览器中使用 debugger 或断点。如果您在 useEffect 调用中放置一个断点,您将很快能够诊断出您的问题。