【问题标题】:Strange bugs with localStorage with useState react带有 useState 的 localStorage 的奇怪错误反应
【发布时间】:2021-12-09 21:26:48
【问题描述】:

我尝试使用 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 };
    });
  }, []);

但它不起作用!

更新:

这里有两个完整的文件:

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

【问题讨论】:

  • 我不确定我是否理解; “setEnteredValue”不区分姓名/电话,也不清楚您如何将更新存储回本地存储。并且初始状态值将始终按照显示的顺序进行。您可能需要提供更多详细信息。
  • 我已经更新了帖子,现在我想我的意思更清楚了,谢谢

标签: reactjs local-storage


【解决方案1】:

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

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

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

},[])

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-26
    • 2018-12-18
    • 2020-04-13
    • 2014-10-06
    • 2014-05-24
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多