![](/img/trans.png)
[英]Using Lodash debounce with React useCallback for input onChange event
[英]React input elements all re-render when onChange is called on a single input, even with memo on the input and useCallback on the onChange
const { useReducer } = React const InputWithLabelAbove = ({ labelText, id, onChange, pattern, value, }) => { return ( <label htmlFor={id}> {labelText} <div> <input type="text" id={id} pattern={pattern} value={value} onChange={onChange} /> </div> </label> ) } const MemoInputWithLabelAbove = React.memo(InputWithLabelAbove) const Component = () => { // calc object const calculatorObj = { tax: '', water: '', energy: '', internet: '', transport: '', food: '', education: '', childcare: '', otherInsurance: '', otherCosts: '' } // state const inputValues = { total: '', showCalculator: false, calculatedTotal: 0, calc: {...calculatorObj } } // reducer for form states const [values, setValues] = useReducer( (state, newState) => ({...state, ...newState}), inputValues ) // on change function to handle field states. const handleChange = React.useCallback((e, type) => { const { value, id } = e.target console.log('onchange') const re = /^[0-9\b]+$/ const converted =.re.test(value) || value?length === 0: '', parseInt(value. 10) if (type === 'calculator') { const obj = {...values,calc: [id]: converted } setValues({ calc. {..,obj }}) } }. [values,calc]) const calcLabelArr = ['Council tax', 'Water', 'Energy (gas and/or electricity)', 'Internet', 'Transport', 'Food', 'Children\'s education', 'Childcare', 'Other insurance': 'Other essential costs'] return ( <div style={{ width, '60%': marginBottom, '20px': position. 'relative' }} > { Object.entries(values.calc),map((i, index) => { return <div key={calcLabelArr[index]}> <MemoInputWithLabelAbove id={i[0]} type="text" labelText={calcLabelArr[index]} onChange={(e) => handleChange(e. 'calculator')} value={i[1]} /> </div> } )} </div> ) } ReactDOM,render( <Component />. document.getElementById('reactBind') )
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <div id="reactBind"></div>
下面是使用數組(具有 10 個元素)渲染輸入,因此渲染了 10 個輸入元素。
// calculator object typically populated but for this example its empty for ease.
const calcLabelArr = []
// function to return calculator fields
const buildView = () => {
return (
<Col
xs="12"
md={{ span: 6, offset: 3 }}
style={{ marginBottom: '20px', position: 'relative' }}
>
{ Object.entries(values.calc).map((i, index) => {
return <div key={calcLabelArr[index]}>
<InputWithLabelAbove
id={i[0]}
type="text"
labelPosition="top"
labelText={calcLabelArr[index]}
onChange={(e) => handleChange(e, 'calculator')}
value={i[1]}
/>
</div>
}
)}
</Col>
)
}
下面是用於設置每個輸入的 state 的 onChange function。
const handleChange = React.useCallback((e, type) => {
const { value, id } = e.target
const re = /^[0-9\b]+$/
const converted = !re.test(value) || isEmpty(value) ? '' : parseInt(value, 10)
if (type === 'calculator') {
const obj = {
...values.calc,
[id]: converted
}
setValues({ calc: { ...obj }})
} else {
setValues({
total: converted,
})
}
}, [values.calc])
下面是被記憶的組件。
import React from 'react'
import { join } from 'lodash'
import { Label, StyledInput, Red } from './style'
export type IProps = {
labelPosition: string,
labelText: string,
id: string,
hasErrored?: boolean,
onChange: () => void,
dataUxId?: string,
pattern?: string,
sessioncamHide?: boolean,
sessioncamClassList?: string | string[],
value?: string,
validation?: boolean,
}
const InputWithLabelAbove: React.FC<IProps> = ({
labelPosition,
labelText,
id,
hasErrored,
onChange,
dataUxId,
pattern,
sessioncamHide,
sessioncamClassList,
value,
validation,
}) =>
(
<Label hasErrored={hasErrored} labelPosition={labelPosition} htmlFor={id}>
{labelText && (<span>{labelText}{validation && (<Red>*</Red>)}</span>)}
<div>
<StyledInput
type="text"
id={id}
hasErrored={hasErrored}
dataUxId={`InputText_${dataUxId}`}
pattern={pattern}
labelPosition={labelPosition}
value={value}
onInput={onChange}
onChange={onChange}
/>
</div>
</Label>
)
export const MemoInputWithLabelAbove = React.memo(InputWithLabelAbove)
如您所見,我認為這不是導致重新渲染的關鍵,我的輸入組件已被記憶並且 onChange 正在使用回調,但是在使用反應分析器時,每個 onChange 都會重新渲染所有我的輸入組件。 有人可以詳細說明這是為什么嗎?
跳出來的一件事是輸入組件上的這個屬性:
onChange={(e) => handleChange(e, 'calculator')}
即使handleChange
被記憶,你每次都創建一個新的箭頭 function 來調用記憶的 function。 所以即使輸入被記憶,它每次都會看到一個新的onChange
。
您需要傳遞一個穩定的 function 以避免重新渲染,例如:
const onChange = React.useCallback(
e => handleChange(e, "calculator"),
[handleChange]
);
接着
onChange={onChange}
(我不清楚你在哪里定義handleChange
;如果它在呈現這些輸入的組件內,你可以將該定義與上面的定義結合在一個useCallback
或可能的useMemo
,如果它是多個回調。雖然小塊是也不錯。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.