首页 > 解决方案 > 带有 useState 的 localStorage 的奇怪错误反应

问题描述

我试图用 localStorage 保存输入的值,但我有一个奇怪的错误,它不会从 localStorage 加载数据。

1-我在InputFile.js的useEffect中用setItem设置了多个输入的数据

    useEffect(() => {
    let validateInputs = {
      fullNameValidate: enteredFullNameIsValid,
      phoneValidate: enteredPhoneIsValid,
    };

    setValidation(validateInputs);

    const allData = { fullNameEntered, phoneEntered };
    localStorage.setItem("data", JSON.stringify(allData));
  }, [
    enteredFullNameIsValid,
    enteredPhoneIsValid,
    setValidation,
    fullNameEntered,
    phoneEntered,
  ]);

2 - 我在自定义钩子(use-input.js)中更新处理程序的值:

const [enteredValue, setEnteredValue] = useState({
    enterValidate: "",
  });

    const valueChangeHandler = (event) => {
    setEnteredValue({
      ...enteredValue,
      enterValidate: event.target.value,
    });
  };

3-我尝试获取保存的值(在自定义挂钩中,use-input.js):

useEffect(() => {
    const saved = localStorage.getItem("data");
    const { fullNameEntered, phoneEntered } = JSON.parse(saved);
    setEnteredValue((prevState) => {
      return { ...prevState, fullNameEntered, phoneEntered };
    });
  }, []);

但它不起作用!

更新:

这里有2个完整的文件:

1- 输入的自定义钩子

    const useInput = (validateValue) => {
  const [enteredValue, setEnteredValue] = useState({
    enterValidate: "",
  });
  const [isTouched, setIsTouched] = useState(false);
  const [clickClasses, setClickClasses] = useState(false);

  const valueIsValid = validateValue(enteredValue.enterValidate);
  const hasError = !valueIsValid && isTouched;

  const valueChangeHandler = (event) => {
    setEnteredValue({
      ...enteredValue,
      enterValidate: event.target.value,
    });
  };

  const inputBlurHandler = () => {
    setIsTouched(true);
    setClickClasses(false);
  };
  const inputClickHandler = () => {
    setClickClasses(!clickClasses);
  };

  const reset = () => {
    setEnteredValue("");
    setIsTouched(false);
  };

////TAKE STORED DATA////// BUT IT DOESN'T WORK

  useEffect(() => {
    const saved = localStorage.getItem("data");
    const { fullNameEntered, phoneEntered } = JSON.parse(saved);
    setEnteredValue((prevState) => {
      return { ...prevState, fullNameEntered, phoneEntered };
    });
  }, []);

  console.log(enteredValue);

  return {
    value: enteredValue.enterValidate,
    isValid: valueIsValid,
    hasError,
    valueChangeHandler,
    inputBlurHandler,
    inputClickHandler,
    click: clickClasses,
    reset,
  };
};

export default useInput;

2- 输入文件

    import React, { useEffect } from "react";
    import useInput from "../../../../../hooks/use-input";
    import validateInput from "../../../../../utils/validateInput";
    import {
      WrapperNamePhone,
      LabelNamePhone,
      SpanInputDescription,
      InputStyle,
    } from "../ContactFormInput.style";
    
    export default function FullNamePhoneInput({ setValidation }) {
  //FullName Input
  const {
    value: fullNameEntered,
    isValid: enteredFullNameIsValid,
    hasError: fullNameHasError,
    valueChangeHandler: fullNameChangeHandler,
    inputBlurHandler: fullNameBlurHandler,
    inputClickHandler: fullNameClickHandler,
    click: fullNameClickClasses,
  } = useInput((value) => {
    const inputValidFullName = {
      value: value,
      maxLength: 20,
      whiteSpace: true,
      allowNumber: false,
      allowStrings: true,
    };

    return validateInput(inputValidFullName);
  });
  //Phone numbers input
  const {
    value: phoneEntered,
    isValid: enteredPhoneIsValid,
    hasError: phoneHasError,
    valueChangeHandler: phoneChangeHandler,
    inputBlurHandler: phoneBlurHandler,
  } = useInput((value) => {
    const inputValidPhone = {
      value: value,
      maxLength: 15,
      whiteSpace: true,
      allowNumber: true,
      allowStrings: false,
    };

    return validateInput(inputValidPhone);
  });

  useEffect(() => {
    let validateInputs = {
      fullNameValidate: enteredFullNameIsValid,
      phoneValidate: enteredPhoneIsValid,
    };

    setValidation(validateInputs);


//STORE DATA////

    const allData = { fullNameEntered, phoneEntered };
    localStorage.setItem("data", JSON.stringify(allData));


  }, [
    enteredFullNameIsValid,
    enteredPhoneIsValid,
    setValidation,
    fullNameEntered,
    phoneEntered,
  ]);

  //FUll NAME
  const borderColorFullName = fullNameHasError ? `rgb(245, 2, 2)` : `#d5d9dc`;

  const clickedColor = fullNameClickClasses ? "#2696e8" : "#a4aeb4";

  //PHONE NUMBERS
  const borderColorPhone = phoneHasError ? `rgb(245, 2, 2)` : `#d5d9dc`;

  return (
    <WrapperNamePhone>
      <LabelNamePhone htmlFor="full-name">
        <SpanInputDescription clickedColor={clickedColor}>
          Full name
        </SpanInputDescription>
        <InputStyle
          type="text"
          name="full-name"
          id="full-name"
          borderColor={borderColorFullName}
          value={fullNameEntered}
          onChange={fullNameChangeHandler}
          onBlur={fullNameBlurHandler}
          onClick={fullNameClickHandler}
        />
        {fullNameHasError && <p> - Enter a valid Full Name</p>}
      </LabelNamePhone>
      <LabelNamePhone htmlFor="phoneNumber">
        <InputStyle
          placeholder="Enter a valid phone number"
          type="text"
          name="phoneNumber"
          borderColor={borderColorPhone}
          value={phoneEntered}
          onChange={phoneChangeHandler}
          onBlur={phoneBlurHandler}
        />
        {phoneHasError && <p> - Enter a valid Phone Number</p>}
      </LabelNamePhone>
    </WrapperNamePhone>
  );
}

///////////////////////////////////////// ///

最终更新,我不喜欢这个解决方案,但它有效!我已经这样做了:

-1 我从输入文件中删除了 setKeys。

-2 我在 use-input 钩子中动态更新 setKeys:

-3 然后使用 useState 我更新 getItem!

    const [enteredValue, setEnteredValue] = useState(() => {
    return {
      validateInput: "",
      fullName: JSON.parse(localStorage.getItem("fullName")) || "",
      phoneNumber: JSON.parse(localStorage.getItem("phoneNumber")) || "",
      email: JSON.parse(localStorage.getItem("email")) || "",
      country: JSON.parse(localStorage.getItem("country")) || "",
      From: JSON.parse(localStorage.getItem("From")) || "",
      To: JSON.parse(localStorage.getItem("To")) || "",
    };
  });

      const valueChangeHandler = (event) => {
    setEnteredValue({
      [event.target.name]: event.target.value,
      validateInput: event.target.value,
    });

    localStorage.setItem(event.target.name, JSON.stringify(event.target.value));
  };

*/ Other code for validation, useless in this example */


  return {
    value: enteredValue.validateInput,
    valueName: enteredValue.fullName,
    valuePhone: enteredValue.phoneNumber,
    valueEmail: enteredValue.email,
    valueCountry: enteredValue.country,
    valueFrom: enteredValue.From,
    valueTo: enteredValue.To,
    isValid: valueIsValid,
    hasError,
    valueChangeHandler,
    inputBlurHandler,
    inputClickHandler,
    click: clickClasses,
    reset,
  };

标签: reactjslocal-storage

解决方案


我不确定我是否完全理解了您的问题,但是您可以使用效果并根据任何依赖项或仅在组件安装上设置其值,而不是在 useState 中执行所有操作,如下所示:

const [enteredValue, setEnteredValue] = useState({});

useEffect(()=>{
const saved = localStorage.getItem("data");
const { fullNameEntered, phoneEntered } = JSON.parse(saved);
setEnteredValue({
  ...enteredValue,
   fullname:fullNameEntered,
   phone:phoneEntered
})

},[])

您可以更改以添加一些条件以确保其在设置之前不为空等。但是这种方法通常应该有效,而不是在 useState 中使用回调。

然后,您可以通过状态 ieenteredValue?.fullname等使用组件中的值(?。是可选链接以防止未定义的错误)


推荐阅读