问题
I'm building contact manager. When the user clicks the update button for a specific contact an action is dispatched and the "hotContact" property in the reducer's state is populated with an object. What I want is the fields of the ContactForm to be populated with the name and number of the "hotContact". However, despite the hotContact being loaded into the redux state my ContactForm component won't display the name and number of the hotContact. How can I proceed? This is what I have so far.
I tried calling setFormData in a conditional block to check if hotContact is present and loadingHotContact is false, but that just gives me an infinite re-render error.
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { addContact, updateContact } from '../actions/contacts';
const ContactForm = ({
addContact,
updateContact,
contacts: { hotContact, loadingHotContact },
}) => {
const [formData, setFormData] = useState({
name:
hotContact === null && loadingHotContact
? ''
: hotContact.name,
number:
hotContact === null && loadingHotContact
? ''
: hotContact.number,
});
const onFormDataChange = (event) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};
const { name, number } = formData;
const handleSubmit = (event) => {
event.preventDefault();
const newContact = { name, number };
addContact(newContact);
console.log('Submit the form!');
setFormData({ name: '', number: '' });
};
const handleUpdateSubmit = (event) => {
event.preventDefault();
const updatedContact = { name, number };
updateContact(hotContact._id, updatedContact);
};
return !hotContact ? (
<form onSubmit={handleSubmit}>
<div>
Name{' '}
<input
type='text'
name='name'
value={name}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<div>
Number{' '}
<input
type='text'
name='number'
value={number}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<input type='submit' value='Add Contact' />
</form>
) : (
<form onSubmit={handleUpdateSubmit}>
<div>
Name{' '}
<input
type='text'
name='name'
value={name}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<div>
Number{' '}
<input
type='text'
name='number'
value={number}
onChange={(event) => onFormDataChange(event)}
/>
</div>
<input type='submit' value='Apply Changes' />
</form>
);
};
const mapStateToProps = (state) => ({
contacts: state.contacts,
});
export default connect(mapStateToProps, { addContact, updateContact })(
ContactForm
);
回答1:
This doesn't work because at the first renderer useState
is initialized with the hotContact
from the props, but when you receive the new value from the props the state doesn't update (that's how the useState
hook works)
If you want to update your state you should use the useEffect
hook:
const ContactForm = ({
addContact,
updateContact,
contacts: { hotContact, loadingHotContact },
}) => {
const [formData, setFormData] = useState({
name:
hotContact === null && loadingHotContact
? ''
: hotContact.name,
number:
hotContact === null && loadingHotContact
? ''
: hotContact.number,
});
useEffect(() => {
const {name, number} = props.hotContact;
setFormData({
name: name || '',
number: number || '',
});
// execute this
}, [hotContact]); // when hotContact changes
}
Also, I think you may simplify you assignment this way:
const {name, number} = props.hotContact;
setFormData({
name: name || '',
number: number || '',
});
来源:https://stackoverflow.com/questions/62459676/updating-functional-component-local-state-using-data-from-redux-state