LocalStorage не обновляет свойство в состоянии с помощью ловушек React - PullRequest
0 голосов
/ 02 мая 2020

Я пытаюсь обновить свойство объекта, ранее объявленное в хуке useState для значений формы, и сохранить его в localstorage. Все идет хорошо, но localstorage постоянно сохраняет свойство даты пустым, я знаю, что это должно быть из-за асинхронности, но я не могу найти решение. Это мой код Я новичок ie с крючками 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 Ответ

1 голос
/ 02 мая 2020

В этом случае проблема заключается в том, что вызванный handleForm все еще имеет доступ только к состоянию formValues ​​во время его вызова, а не к новому состоянию. Итак, самый простой способ справиться с этим - просто обновить formValues, setFormValues, а затем setTweetList на основе локальной копии обновленных formValues.

  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 вместо двух отдельных переменных состояния, потому что они являются связанными свойствами, и вы сможете легко обновлять их на основе друг друга.

В качестве примера более сложных обновлений с помощью редукторов, Я добавил действие '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;
  }
}
...