iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0
Modern Web

React 新手村 - 填坑記系列 第 13

React 新手村 - 填坑記 - Day13 開發問題(九)

  • 分享至 

  • xImage
  •  

React Hook useState 常見問題

1. 異步回調不即時

使用useState數據進行更新時,不能即時獲得最新數據。

const [name, setName] = useState('dp')
const handleTest = () => {
    console.log(name) //dp
    setName('dx1')
    console.log(name) //dp
}

解決方法:
一、使用useEffect

useEffect(() => {
  console.log(name) //dx1
},[name])

二、建立新的變數保存新數據

const [name, setName] = useState('dp');
const handleTest = () => {
  console.log(name) //dp
  const newName = "dx1"
  setName(newName)
  console.log(newName) //dx1
}

2. 使用位置有限制

* 僅能在最上層調用Hook,不能在循環(map)、巢狀component...等中調用useState()。
* 多個useState()調用時,渲染調用順序必須相同。
* 僅能從React函數組件(component)或自定義Hook中調用useState()。

3.回調更新數據

const [a, setA] = useState({c:1})
// oldA之前的a,return設定的新值
setA((oldA) => {
  return {c: oldA.c + 1}
})

4.如果只是保存數據,useEffect需調整才有辦法檢測到變化

const contentObj = {name:'dp'}
const [content, setContent] = useState(contentoObj)
setContent((oldContent) => {
    oldContent.age = 18
    return {oldContent} // 返回新的數據,useEffect才能檢測到變化
})

5.產生多次渲染問題

以下Code會在頁面渲染兩次

const [isLoading, setIsLoading] = useState(false)
const [data, setData] = useState([])
useEffect(async() => {
    const res = await axios.get("XXX")
    setIsLoading(false)
    setData(res)
},[])

在React中,同步Code會進行合併渲染,異步則不會合併,而以下Code則只會渲染一次

const [isLoading, setIsLoading] = useState(false)
const [data, setData] = useState([])
useEffect(() => {
    setIsLoading(false)
    setData({a:1})
},[])

但如果依然需要使用到異步那就需要使用以下幾種方式
方法一、多個狀態合併至一個狀態

const [request, setRequest] = useState({ isLoading: true, data: null });
useEffect(async () => {
  const res = await axios.get("xxx");
  setRequest({ isLoading: false, data: res });
}, []);

但如果單純只想setState一個值時,會需要將其他值也傳進去,否則將會遺失。React內部並不會自動幫你做合併

setRequest({ data: res }); // isLoading 值遺失了。

//解決方法使用展開運算符(Spread Operator)
setRequest({ ...request, data: res });
// 或者
setRequest((prevState) => ({ ...prevState, data: res }));

方法二、自定義一個合併值的Hook

const useMergeState = (initialState) => {
  const [state, setState] = useState(initialState);
  const setMergeState = (newState) =>
    setState((prevState) => ({ ...prevState, newState }));
  return [state, setMergeState];
};

/* 使用 */
const [request, setRequest] = useMegeState({ isLoading: false, data: null });
useEffect(async () => {
  const res = await axios.get("xxx");
  setRequest({ isLoading: true, data: res });
  // ...
  setRequest({ data: { a: 1 } }); // isLoading 狀態不會遺失,而且值仍是 true
}, []);

方法三、使用useReducer來管理各值,而非useState

const [request, setRequest] = useReducer(
  (prevState, newState) => {
    const newWithPrevState = typeof newState === "function" ? newState(prevState) : newState;
    return{ ...prevState, newWithPrevState })
    },
  { isLoading: false, data: null }
);
useEffect(async () => {
  const res = await axios.get("xxx");
  setRequest((prevState) => {
    console.log(prevState)
    return { isLoading: true, data: res }
  });
  // ...
  setRequest({ data: { a: 1 } }); // isLoading 狀態不會遺失,而且值仍是 true
}, []);

上一篇
React 新手村 - 填坑記 - Day12 開發問題(八)
下一篇
React 新手村 - 填坑記 - Day14 開發問題(十)
系列文
React 新手村 - 填坑記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言