我正在尝试更新先前在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]);
答案 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;
}
}