[英]Child component of a connected component using redux connect doesn't re-render after one prop change
[英]Children prop triggering re-render when using map and redux
我正在使用Redux創建一個測驗應用程序,其中包括帶有一些嵌套字段的表單。 我剛剛意識到(我認為),如果我使用children道具,則每次按下輸入字段都會觸發重新渲染,即按如下方式設計應用程序:
const keys = Object.keys(state)
<QuizContainer>
{keys.map(key =>
<QuizForm key={key}>
{state[key].questions.map(({ questionId }) =>
<Question key={questionId} questionId={questionId}>
{state[key]questions[questionId].answers.map(({ answerId })=>
<Answer answerId={answerId} key={answerId} />
)}
</Question>
)}
</QuizForm>
)}
</QuizContainer>
QuizContainer使用mapStateToProps和mapDispatchToProps連接到redux,並吐出一個數組數組,這些數組中都包含對象。 商店結構是根據Dan Abramov的“ Redux嵌套指南”(使用類似於關系數據庫的商店類型)設計的,可以這樣描述。
{
['quizId']: {
questions: ['questionId1'],
['questionId1']:
{ question: 'some question',
answers: ['answerId1', 'answerId2']
},
['answerId1']: { some: 'answer'},
['answerId2']: { some: 'other answer'}
}
上面的代碼可以正常工作,沒有任何錯誤,但是會引發大量的重新渲染,但是只有在我使用合成語法的情況下,以上代碼才能正常工作。 如果我將每個組件放到另一個組件中(即不使用props.children),而只是發送quizId-key(以及其他id-number作為props),它將按預期工作-無需瘋狂地重新渲染。 為了清晰起見,當我執行以下操作時它會起作用:
// quizId and questionId being passed from parent component's map-function (QuizForm)
const Question ({ answers }) =>
<>
{answers.map(({ answerId }) =>
<Answer key={answerId} answerId={answerId} />
)}
</>
const mapStateToProps = (state, { questionId, quizId }) => ({
answers: state[quizId][questionId].answers
})
export default connect(mapStateToProps)(Question)
但為什么? 兩者有什么區別? 我意識到其中一個是作為道具傳遞給父母的,而不是被渲染為那個父母的孩子,可以這么說,但是為什么這樣做最終會帶來不同的結果呢? 它們不應該相等但可以使用更好的語法嗎?
編輯:我現在可以驗證兒童道具是否引起了問題。 設置
shouldComponentUpdate(nextProps) {
if (nextProps.children.length === this.props.children.length) {
return false
} else {
return true
}
}
解決問題。 但是,這似乎是一個非常好的黑匣子解決方案,不太確定我現在錯過了什么...
使用render prop模式時,每次組件渲染時都在有效地聲明一個新函數。 因此, props.children
之間的任何淺層比較都會失敗。 這是該模式的一個已知缺點 ,您的“黑匣子”解決方案是有效的。
好吧,我明白了:
我做了兩件事:
我將頂部組件連接到狀態,如下所示:mapStateToProps(state)=>({keys:Object.keys(state)})。 我以為對象函數將返回“靜態”數組並阻止我偵聽整個狀態,但事實證明我錯了。 顯然(現在對我來說),每次更改狀態時,我都會得到一個新的數組(但具有相同的條目)。 現在,我將它們存儲在一個完全獨立的屬性quizIds中。
我將地圖功能放在不好的地方。 我現在繼續像這樣渲染QuizContainer:
<QuizContainer> {quizIds.map(quizId => <QuizForm> <Question> <Answer /> </Question> </QuizForm> )} </QuizContainer>
然后,我渲染我的孩子數組,為他們注入道具以便能夠單獨使用connect,如下所示:
{questions.map((questionId, index) => (
<React.Fragment key={questionId}>
{React.cloneElement(this.props.children, {
index,
questionId,
quizId
})}
</React.Fragment>
))}
如果您決定將多個元素作為子元素,則最后一段代碼將不起作用。 無論如何,看起來更干凈,現在效果更好! :D
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.