LocalStorage不使用React挂钩更新状态中的属性

时间:2020-05-02 14:30:39

标签: reactjs react-hooks use-effect

我正在尝试更新先前在useState挂钩中声明的对象属性以获取表单值,并将其保存在localstorage中。一切顺利,但是localstorage始终将date属性保存为空,我知道这一定是因为异步,但是我找不到解决方案。这是我的代码。我是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]);

1 个答案:

答案 0 :(得分:1)

在这种情况下,问题是因为被调用的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;
  }
}