Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
181
rated 0 times [  185] [ 4]  / answers: 1 / hits: 5551  / 5 Years ago, tue, november 19, 2019, 12:00:00

I have a component with an empty metadata object at DOM load, the server sends data to fill the empty metadata object with properties that will be assigned values within the form. I am able to iterate through the meta data and see multiple input fields correctly labeled yet when I got to input something it either doesn't change anything and the console logs the single keystroke or it returns TypeError: Cannot read property 'handleChange' of undefined. The title field handles the change just fine.



My code:



class Item extends React.Component{
constructor(props) {
super(props);
this.state = {
title: '',
metadata: {}
}
}

componentDidMount() {
... //retrieve metadata from server
this.setState({
metadata: metadata
});
console.log(metadata); //{meta1: , meta2: , meta3: , meta4: , meta5: , …}
}

handleChange = (field) => {
return (value) => this.setState({ [field]: value });
}

render() {
const {
title,
metafield
} = this.state;
}

return(
//code to start form

<TextField value={title} onChange={this.handleChange(title)} label=Title type=text />

{Object.keys(metadata).map(function(key) {
return (
<TextField key={key} value={metadata[key]} onChange={this.handleChange({key})} label={key} type=text />
)
})}

//code to end form
)
}


I'm sure it's because the handleChange isn't equipped to handle changes on object properties but I'm not sure how to access that layer. I've tried binding a handleMetadataChange function on the constructor and use e.target to assign the values but the failing behavior persists.


More From » arrays

 Answers
2

There are a couple of bugs:




  1. handleChange sets state like this: this.setState({ [field]: value}); but the values are in state.metadata not in state.

  2. In render
    you get metafield from state but initially you set metadata
    and in handleChange you don't use any of it.

  3. You always re create onChange for TextField even if nothing has changed, this causes needless DOM re renders.



Here is a working example:





class App extends React.Component {
constructor(props) {
super(props);
this.state = {
metadata: {},
};
}

componentDidMount() {
Promise.resolve().then(() =>
this.setState({
metadata: { x: 'x', y: 'y' },
})
);
}

handleChange = (field, value) =>
//you forgot you are setting metadata of state
this.setState({
...this.state,
metadata: { ...this.state.metadata, [field]: value },
});

render() {
const {
metadata, //you used metaField here but it's metadata
} = this.state;
return (
<div>
{Object.keys(metadata).map(key => (
<TextField
key={key}
value={metadata[key]}
onChange={this.handleChange} //always pass the same handler function
changeKey={key} //added for optimization
label={key}
/>
))}
</div>
);
}
}
//make textfield a pure component as it only receives props
// You could call this TextFieldContainer and not change TextField at all
const TextField = React.memo(function TextField({
value,
onChange,
changeKey,
label,
}) {
const rendered = React.useRef(0);
rendered.current++;
return (
<div>
times rendered: {rendered.current}
<label>
{label}
<input
type=text
value={value}
onChange={e =>
onChange(changeKey, e.target.value)
}
/>
</label>
</div>
);
});

ReactDOM.render(<App />, document.getElementById('root'));

<script src=https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js></script>
<div id=root></div>




[#5546] Sunday, November 17, 2019, 5 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
casandra

Total Points: 334
Total Questions: 93
Total Answers: 104

Location: Denmark
Member since Tue, Jul 19, 2022
2 Years ago
casandra questions
Thu, Feb 25, 21, 00:00, 3 Years ago
Mon, Jul 6, 20, 00:00, 4 Years ago
Thu, May 21, 20, 00:00, 4 Years ago
Tue, Sep 17, 19, 00:00, 5 Years ago
Sat, Jan 19, 19, 00:00, 5 Years ago
;