請問我使用react寫了個Login.js component以及Message.js這個共用的component,當在Login時Message會根據response去顯示訊息,useState(hook)去控制Message是否要顯示,可是useState只會在第一次render初始狀態時用到,第二次要render時,我想說利用Login傳來的props.open去判斷訊息顯示,可是這樣碰到兩個問題:
1.官方建議不要在判斷式中使用hook
2.顯示Too many re-renders. React limits the number of renders to prevent an infinite loop.(Error)
Login.js(省略部分code)
const [response, setResponse] = useState('')
const handleSubmit = async e => {
e.preventDefault()
let response = await instance('post', `/api/user/login`, '', values)
setResponse(response)
}
{response ?
<Message open={true}
message={response.data.status === 400 ? response.data.error[0].message : 'Login successfully!'}
timeout={3000}
/>
: null}
Message.js
export default props => {
const [open, setOpen] = useState(props.open)
if (props.open === true) setOpen(true)
return (
<Snackbar open={open}
onClose={() => setOpen(false)}
// TransitionComponent={transition}
autoHideDuration={props.timeout}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={props.message ? props.message : ''}
/>
)
}
不確定你想做什麼,如果是單就這段的話:
useState只會在第一次render初始狀態時用到,第二次要render時,我想說利用Login傳來的props.open去判斷訊息顯示
import React, {useState,useRef,useEffect} from 'react';
const mounted=useRef();
// 利用useEffect處理每次render時要做的事情,並用useRef紀錄是否為第一次render
useEffect(()=>{
if(mounted.current===false){
mounted.current=true;
}
else{
if (props.open === true) setOpen(true)
}
},[props.open])
但看了一下,你好像是希望初始時和props.open被改變時就要setOpen(props.open)。如果是這樣的話這樣就好了。
useEffect(()=>{
setOpen(props.open)
},[props.open])
我的想法只是想要讓他有訊息時,open為true,可是我有使用autoHideDuration這個屬性,時間到了會去跑onClose,這時open就變為false了,那我再送出一次表單,想要再顯示一次message時,不知在哪加setOpen(true)比較好,大大的方法我試了好像也不行
「再送出一次表單」和「有訊息」這兩個動作發生時,去改變在綁在Message上的open。
然後不是在useEffect中的判斷式要刪掉
你可以把props.open在第一次之後當作是一個「觸發useEffect」的開關。它的值不再具有意義,而是讓useEffect偵測到它被改變之後,顯示你的訊息。
useEffect(()=>{
setOpen(true)
},[props.open])
而在Login.js中,也用一個state去綁定open(假設是controlOpen
),當你有新回應進來,呼叫此函式:
setControlOpen(!controlOpen);
我在Login.js裡加
const [controlOpen, setControlOpen] = useState(false)
const handleSubmit = async e => {
e.preventDefault()
let response = await instance('post', `/api/user/login`, '', values)
setResponse(response)
setControlOpen(!controlOpen)
}
可是我這樣按submit之後第一次有訊息,如果馬上再按一次submit controlOpen不是就變回false了?
對。因為假設你是在Login.js接收新回應/submit。你想在「接收新回應/submit」時更改message,實質上這個動作等同於父元件對子元件的主動溝通。也不一定要用open這個props,只是因為剛好你的props.open在第一次設定完初始值後就沒啥用了,所以才用它。你要綁在另外一個新的props也可以。總之我們只是需要一個用來改變的props,然後讓message裡的useEffect能偵測這個props並藉由這個props的改變而得知「我們要打開message」並執行這個動作。 而controlOpen作為這個props,它第一次之後是啥值都沒差,只要有改變就好。
意思是這樣也是沒差,只是這樣你open後面都沒用到。
const [controlOpen, setControlOpen] = useState(1)
const handleSubmit = async e => {
e.preventDefault()
let response = await instance('post', `/api/user/login`, '', values)
setResponse(response)
setControlOpen(controlOpen+1)
}
return (
{response ?
<Message open={true} control={controlOpen}
message={response.data.status === 400 ? response.data.error[0].message : 'Login successfully!'}
timeout={3000}
/>
: null}
/>
)
message
useEffect(()=>{
setOpen(true)
},[props.control])
我懂您的意思,就是在props中隨便加入一個屬性值,就大大的範例來說(props.controlOpen),然後每次submit時去改變它,接著在Message裡useEffect就會根據props.controlOpen的改變去作動
Login.js
const [open, setOpen] = useState(false)
const handleSubmit = async e => {
e.preventDefault()
let responses = await instance('post', `/api/user/login`, '', values)
setResponse(responses)
setOpen(!open)
}
{response ?
<Message message={response.data.status === 400 ? response.data.error[0].message : 'Login successfully!'}
timeout={3000}
control={open}
/>
: null}
Message.js
export default props => {
const [open, setOpen] = useState(true)
useEffect(() => {
setOpen(true)
}, [props.control])
return (
<Snackbar open={open}
onClose={() => setOpen(false)}
// TransitionComponent={transition}
autoHideDuration={props.timeout}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={props.message ? props.message : ''}
/>
)
}
Yep 這樣應該可以解決你想實現的東西(吧?)
最近剛好也在學react
不過我覺得class比較好用
你的問題應該是在這裡
if (props.open === true) setOpen(true)
不太懂你useState()的時候就已經綁 props.open了
為何後面還要再加這個判斷式呢?
初始=props.open
=> 當props.open===true時
=> props.open=true
這樣不就無窮迴圈了嗎?
我的流程是這樣:
1.表單送出(登入失敗)
2.跳出Message
3.autoHideDuration這個屬性時間到會去執行onClose裡的fun,也就是() => setOpen(false)
4.再送出一次表單(還是登入失敗),第二次不會再去執行useState(props.open)
我要怎麼再把open的值變成true呢?
就是setOpen(true)
但你第一次render的時候就進無窮迴圈啦
依你的需求改成useState(false)就好了
而你之後的if (props.open === true) setOpen(true)
會幫你改狀態的
改了還是噴Too many re-renders. React limits the number of renders to prevent an infinite loop.