LocalStorage 未使用 React 挂钩更新 属性 状态

LocalStorage not updating property in state with React hooks

我正在尝试更新一个对象 属性 之前在表单值的 useState 挂钩中声明并将其保存在本地存储中。一切顺利,但是 localstorage 一直在保存日期 属性 为空,我知道这一定是因为异步但我找不到解决方案。这是我的代码。我是 React 钩子的新手。非常感谢!

  const [formValues,setformValues] = useState(
    {
      userName:'',
      tweetText:'',
      date:''
    }
  )

  const getlocalValue = () => {
    const localValue = JSON.parse(localStorage.getItem('tweetList'));
    if(localValue !== null){
      return localValue
    } else {
      return []
    }
  }

  const [tweetList,setTweetList] = useState(getlocalValue());

  const handleInput = (inputName,inputValue) => {
    setformValues((prevFormValues) => {
      return {
        ...prevFormValues,
        [inputName]:inputValue
      }
    })
  }

  const handleForm = () => {
    const {userName,tweetText} = formValues;
    if(!userName || !tweetText) {
      console.log('your tweet is empty');
    } else {
      setformValues(prevFormValues => {
        return {
          ...prevFormValues,
          date:getCurrentDate() //this is not updating in local
        }
      })
      setTweetList(prevTweets => ([...prevTweets, formValues]));
      toggleHidden(!isOpen)
    }

  }
  console.log(formValues) //but you can see changes outside the function


  useEffect(() => {
    localStorage.setItem('tweetList', JSON.stringify(tweetList));
  }, [tweetList]);

在这种情况下,问题是因为被调用的 handleForm 仍然只能访问调用时的 formValues 状态,而不是新状态。因此,处理此问题的最简单方法是仅更新 formValues、setFormValues,然后根据更新后的 formValues 的本地副本设置 setTweetList。

  const handleForm = () => {
    const {userName,tweetText} = formValues;
    if(!userName || !tweetText) {
      console.log('your tweet is empty');
    } else {
      const updatedFormValues = {...formValues,date:getCurrentDate()};
      setformValues(updatedFormValues)
      setTweetList(prevTweets => ([...prevTweets, updatedFormValues]));
      toggleHidden(!isOpen)
    }
  }

因为这里存在并发问题:即您不能保证使用最新数据更新 formValues 和 tweetList 的状态。另一种选择是 useReducer 而不是两个单独的状态变量,因为它们是相关的属性,您可以更轻松地根据彼此更新它们。

作为使用 reducer 进行更复杂更新的示例,我添加了一个 'FINALIZE_TWEET' 操作,它将同时执行操作的两个部分。

const Component = () => {
  const [{ formValues, tweetList }, dispatch] = useReducer(
    reducer,
    undefined,
    getInitState
  );

  const handleInput = (inputName, inputValue) => {
    dispatch({ type: 'SET_FORM_VALUE', payload: { inputName, inputValue } });
  };

  const handleForm = () => {
    const { userName, tweetText } = formValues;
    if (!userName || !tweetText) {
      console.log('your tweet is empty');
    } else {
      dispatch({ type: 'SET_FORM_DATE' });
      dispatch({ type: 'PUSH_TO_LIST' });
      // OR
      // dispatch({type: 'FINALIZE_TWEET'})
      toggleHidden(!isOpen);
    }
  };
  console.log(formValues); //but you can see changes outside the function

  useEffect(() => {
    localStorage.setItem('tweetList', JSON.stringify(tweetList));
  }, [tweetList]);

  return <div></div>;
};

const getlocalValue = () => {
  const localValue = JSON.parse(localStorage.getItem('tweetList'));
  if (localValue !== null) {
    return localValue;
  } else {
    return [];
  }
};
function getInitState() {
  const initialState = {
    formValues: {
      userName: '',
      tweetText: '',
      date: '',
    },
    tweetList: getlocalValue(),
  };
}

function reducer(state, action) {
  switch (action.type) {
    case 'SET_FORM_VALUE':
      return {
        ...state,
        formValues: {
          ...state.formValues,
          [action.payload.inputName]: action.payload.inputValue,
        },
      };
    case 'SET_FORM_DATE':
      return {
        ...state,
        formValues: {
          ...state.formValues,
          date: getCurrentDate(),
        },
      };
    case 'PUSH_TO_LIST':
      return {
        ...state,
        tweetList: [...state.tweetList, state.formValues],
      };
    case 'FINALIZE_TWEET': {
      const newTweet = {
        ...state.formValues,
        date: getCurrentDate(),
      };
      return {
        ...state,
        formValues: newTweet,
        tweetList: [...state.tweetList, newTweet],
      };
    }
    default:
      return state;
  }
}