1

I have some inputs which I render from an array of objects.

When I type in one input, I map over the array, find the corresponding object and set the input value.

The problem: Type some characters in one input, change input, type characters. Repeat this and observe the previous inputs are cleared and sometimes some random character appears in some inputs.

What is causing this behavior?

If I remove the React.memo() from SearchBar, the problem does not appear anymore.

What do I need to do to make it work with React.memo().

const shouldSkipComponentUpdate = (prevProps, props) => prevProps.value === props.value; const SearchBar = React.memo((props) => { const { label, width, hasSearch, handleSearchChange, value } = props; return ( <div width={width} style = { { margin: "15px", display: "inline" } }> {label} {hasSearch && ( <SearchInput onChange={handleSearchChange} value={value} label={label} /> )} </div> ); }, (prevProps, props) => shouldSkipComponentUpdate(prevProps, props)); function DevicesContainer() { const [searchBoxes, setSearchBoxes] = React.useState(() => { return [ { id: 1, label: '', width: '5%', hasSearch: false, value: '' }, { id: 2, label: 'ID', width: '5%', value: '', hasSearch: false }, { id: 3, width: '10%', label: 'Name', value: '', hasSearch: true }, { id: 4, width: '10%', label: 'Owner', value: '', hasSearch: true }, { id: 5, width: '7%', label: 'Availability', value: '', hasSearch: false }, { id: 6, width: '10%', label: 'Location', value: '', hasSearch: true }, { id: 7, width: '20%', label: 'Environment', value: '', hasSearch: true }, { id: 8, width: '10%', label: 'Firmware', value: '', hasSearch: true }, ]; }); function handleSearchChange(event, label) { const { target: { value } } = event; const updated = searchBoxes.map(elem => { if (elem.label === label) { return { ...elem, value }; } return elem; }); setSearchBoxes(updated); } return ( <main> <SearchBars searchBars={searchBoxes} handleSearchChange={handleSearchChange} /> </main> ); } function SearchBars(props) { const { searchBars, handleSearchChange } = props; return ( <div style={ { margin: '20px' } }> {searchBars.map(elem => ( <SearchBar key={elem.id} label={elem.label} width={elem.width} hasSearch={elem.hasSearch} value={elem.value} handleSearchChange={handleSearchChange} /> ))} </div> ); } function SearchInput(props) { const { onChange, value, label } = props; return ( <input type="search" value={value} placeholder="Search" onChange={event => onChange(event, label)} /> ); } ReactDOM.render( <DevicesContainer />, document.getElementById('root') ); 

codepen link https://codepen.io/Apoptotic/pen/ZdPpQG

1 Answer 1

1

You can change the handleSearchChange() function. See the docs on the setState behaviour. You might also note, that this function runs asynchronously. I just moved the mapping function into the setSearchBox update handle to apply the data to the previous state.

 function handleSearchChange(event, label) { const { target: { value } } = event; setSearchBoxes((prevSearchBoxes) => { let s = [...prevSearchBoxes]; let updated = s.map(elem => { if (elem.label === label) { return { ...elem, value }; } return elem; }); return updated; }); } 

Additionally see the following note from the React Docs.

Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:

setState(prevState => { // Object.assign would also work return {...prevState, ...updatedValues}; }); 

Another option is useReducer, which is more suited for managing state objects that contain multiple sub-values.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.