提交表单后reactjs清理输入

时间:2021-04-29 00:08:34

标签: reactjs

目前我的表单中有一个问题,提交表单后我无法清除输入.. 我有两个用于输入和表单的文件:

form-hook.js

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];
};

这里我得到了一个 Input.js 文件:

import React, { useReducer, useEffect } from 'react';

import { validate } from '../../util/validators';

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, clearInputs } = props;
  const { value, isValid } = inputState;

  useEffect(() => {
    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'
    });
  };

  const element =
    props.element === 'input' ? (
      <input
        id={props.id}
        type={props.type}
        placeholder={props.placeholder}
        onChange={changeHandler}
        onBlur={touchHandler}
        value={inputState.value}
        maxLength={props.maxlength}
      />
    ) : (
      <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;

我的问题是我无法访问这个 inputState,看起来值存储在其中... 这只是一个例子,如何在我的组件中使用它,这是 reactjs 中的函数式编程..

import React, { useState, useContext } from 'react';

import { Text, LanguageContext } from '../../lang/containers/Language';
import Input from '../../shared/components/FormElements/Input';
import Button from '../../shared/components/FormElements/Button';
import ErrorModal from '../../shared/components/UIElements/ErrorModal';
import {
    VALIDATOR_EMAIL
} from '../../shared/util/validators';
import { useForm } from '../../shared/hooks/form-hook';
import { useHttpClient } from '../../shared/hooks/http-hook';

const NewSletter = () => {
    const { dictionary } = useContext(LanguageContext);
    const { isLoading, error, sendRequest, clearError } = useHttpClient();
    const [joined, setJoined] = useState(false);

    const [formState, inputHandler] = useForm(
        {
          email: {
            value: '',
            isValid: false
          }  
        },
        false
    );
    
    const newsletterSubmit = async event => {
        event.preventDefault();
        try {
          const responseData = await sendRequest(
            'http://localhost:5000/api/users/newsletter',
            'POST',
            JSON.stringify({
              email: formState.inputs.email.value,
            }),
            {
              'Content-Type': 'application/json'
            }
          );

          if(responseData.message == 'subscribed'){
            setJoined(true);
            //here i need to clean all inputs in this form
          }

        } catch (err) {
          console.log(err)
        }
    };

    return (
    <>
      <ErrorModal error={error ? <Text tid={error}/> : null} onClear={clearError} />
      <div className="newsletter-bg">
                <div className="container">
                    <div className="newsletter-bg-title"><Text tid="newsletter"/></div>

                    <div className="space10px"></div>

                    <form className="newsletter-form" onSubmit={newsletterSubmit}>
                    <Input
                        element="input"
                        id="email"
                        type="email"
                        label={<Text tid="auth_lang4" />}
                        validators={[VALIDATOR_EMAIL()]}
                        errorText={<Text tid="auth_lang5" />}
                        onInput={inputHandler}
                    />
                    <Button type="submit" disabled={!formState.isValid}>
                    <Text tid="add"/>
                    </Button>
                    {joined === true ? <div className="biltenmsg"> <span style={{color: 'green'}}><Text tid="joinedsletter"/></span> </div> : null}
                    </form>
                </div>
      </div>
    </>
    );
};

export default NewSletter;

我还尝试了这里的几个示例,如 reset() 和其他东西,也尝试更改 useForm 的内容,但我无法删除值,因为它从 inputState 中的 input.js 文件中读取值,我怎么想...我也尝试过setformdata 和其他一些示例,但我没有解决这个问题.. 最好的问候。

2 个答案:

答案 0 :(得分:1)

只能通过 dispatch 更新 inputState

    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
          };
        }
        case 'RESET': {
          return {
              value: action.value,
              isTouched: action.isTouched,
              isValid: action.isValid
          };
        }
        default:
          return state;
      }
    };

然后

      dispatch({
         type: 'RESET',
         value: props.initialValue || '',
         isTouched: false,
         isValid: props.initialValid || false
      });

答案 1 :(得分:0)

我在您的 formReducer 中没有看到太多问题,它被 useForm 使用,并且您在内部定义了一个 inputHandler,它是获得任何 input 工作所必需的.但是,恕我直言,您的 Input 过于复杂。

附上一个例子useForm


const useForm = (props = {}) => {
  const [state, dispatch] = useReducer(reducer, initialState(props))

  const change = useCallback((payload) => {
    dispatch({ type: 'change', payload })
  }, [])

  const validate = useCallback((payload) => {
    if (!state.validate) return
    dispatch({ type: 'validate', payload: payload || state.values })
  }, [state])

  const submit = useCallback((payload) => {
    dispatch({ type: 'submit', payload })
  }, [])

  const onChange = useCallback(e => {
    const target = e.target
    const payload = { [target.name]: target.value }
    change(payload)
    validate(payload)
  }, [change, validate])

  const onSubmit = useCallback(e => {
    if (e) e.preventDefault()
    submit(state.values)
  }, [submit, state.values])

  /**
   * @typedef {object} useFormObject
   * @property {string} name        Form name
   * @property {object} values      Form values
   * @property {func} change        Form change function
   * @property {object} handlers    Form events
   * @property {func} handlers.onChange Event upon input change
   * @property {func} handlers.onSubmit Event upon form submit
   * @property {func} validate      Form validate function
   * @property {object} errors      Form errors
   * @property {bool} error         Form in error
   */
  return {
    name: state.name,
    values: state.values,
    change,
    handlers: {
      onChange,
      onSubmit
    },
    validate,
    errors: state.errors,
    error: hasError(state.errors),
  }
}

export default useForm

用法是

  const { values, handlers, errors } = useForm({ initialValues })

然后每个输入小部件都是关于

  <input value={values.abc} onChange={handlers.onChange} />

您可以看到在 reducer 中不需要另一个级别的 input,因为 valueonChange 几乎都是从输入点开始的“只读”属性查看。

为完整起见,此处附上 reducer

import hasError from './hasError'

const defaultState = {
  values: {},
  errors: {},
  watchers: {}
}

const reducer = (state = defaultState, action) => {
  let payload = {}
  if (action && action.payload) {
    payload = action.payload
  }

  const watchers = state.watchers || {}
  const watch = state.watch || (() => { })
  const validate = state.validate || (() => { })
  const { onSubmitted } = watchers
  let errors
  switch (action.type) {
    case 'change':
      watch(action.type, payload)
      return {
        ...state,
        values: {
          ...state.values,
          ...payload
        }
      }
    case 'validate':
      errors = validate(payload, action.type)
      watch(action.type, errors)
      return {
        ...state,
        errors: {
          ...state.errors,
          ...errors
        }
      }
    case 'submit':
      errors = validate(payload, action.type)
      watch('validation', errors)
      if (hasError(errors)) {
        return {
          ...state,
          errors
        }
      }
      if (onSubmitted) {
        onSubmitted(state.values)
      }
      return state
    default:
      return state
  }
}

export default reducer

相关问题