I'm trying to map a redux-form's initialValues
property so that the form can be pre-populated (this is a user profile edit screen that is populated from an external API).
Because the validation of what fields the user is allowed to manage occurs on the API, the React JS frontend needs to be as dynamic as possible when it comes to populating the initial values. I did find another question here on SO that addressed dynamically pre-populating initialValues
(Dynamically load initialValues in Redux Form), however that solution would only work for objects that have no nesting. Unfortunately, my response from the API does contain some nesting:
{
"id": 1,
"email": "admin@test.com",
"location_id": 2,
"created_at": "2017-09-08 19:55:01",
"updated_at": "2017-09-08 19:55:01",
"can_edit": [ // <-- this array contains all the fields that the given user is allowed to edit on the given profile.
"profile_img", // <-- possibly this could be rewritten to return for example 'profile.profile_img' rather than just 'profile_img', but not sure how that could help
"password" // <-- not returned in the actual user object for obvious reasons, but there is logic for displaying the password field anyway
],
"profile": {
"user_id": 1,
"first_name": "Admin",
"last_name": "Test",
"profile_img": "https://pbs.twimg.com/profile_images/449750767281254400/M_ukevnA.jpeg",
"position": "Admin",
"created_at": "2017-09-13 11:38:49",
"updated_at": "2017-09-13 11:38:49",
},
"location": {
"id": 2,
"text": "Test Location",
"lang": "en",
"created_at": "2017-09-13 11:35:41",
"updated_at": "2017-09-13 11:35:41",
"deleted_at": null
}
}
So I can't simply populate initialValues directly - since, for example, possible editable fields include the email
or location_id
of the root user object, or the first_name
, last_name
, or profile_img
properties of the nested profile object.
For reference, my EditProfile component is below (truncated to the relevant portions).
class EditProfile extends Component {
/**
* Static property types.
*/
static propTypes = {
...
}
/**
* Renders editable profile fields.
*/
profileFields(user) {
if(!this.state.currentField.length){
this.state.currentField = user.can_edit[0]
}
let fields = user.can_edit.map((can_edit) => {
switch(can_edit){
// render an email input
case 'email':
return (
<ProfileFieldGroup key={can_edit} show={ this.state.currentField == can_edit }>
<label htmlFor="email">Email</label>
<Field
name="email"
id="email"
type="email"
component="input"
value={ user.email }
/>
</ProfileFieldGroup>
)
// render a group of password inputs
case 'password':
return (
<ProfileFieldGroup key={can_edit} show={ this.state.currentField == can_edit }>
<label htmlFor="old_password">Old Password</label>
<Field
name="old_password"
id="old_password"
type="password"
component="input"
/>
<label htmlFor="password">New Password</label>
<Field
name="password"
id="password"
type="password"
component="input"
/>
<label htmlFor="password_confirm">Confirm New Password</label>
<Field
name="password_confirm"
id="password_confirm"
type="password"
component="input"
/>
</ProfileFieldGroup>
)
// by default, create a text input whose ID and name match the given property
default:
console.log(user.profile[can_edit])
return (
<ProfileFieldGroup key={can_edit} show={ this.state.currentField == can_edit }>
<label htmlFor={can_edit}>{ humanizeText(can_edit) }</label>
<Field
name={ can_edit }
id={ can_edit }
type="text"
component="input"
value={ user.profile[can_edit] }
/>
</ProfileFieldGroup>
)
}
})
return (
<div>
{ fields }
</div>
)
}
/**
* Handles the submit event.
* This function is mainly managed by Redux.
*/
submit = (values) => {
...
}
/**
* Renders the component.
*/
render() {
const {
handleSubmit,
user: user,
save: save
} = this.props
return (
<section>
<header>
<h1>Edit User</h1>
</header>
<div className="main">
<form onSubmit={ handleSubmit(this.submit) }>
{user.successful && !user.requesting &&
this.profileFields(user.user)
}
{user.requesting && !user.successful && (
<img className="loader" src=""/>
)}
</form>
</div>
<footer>
</footer>
</section>
)
}
}
And then the connection to redux:
const mapStateToProps = state => ({
user: state.user,
save: state.save,
initialValues: state.user.user // <-- this right here
})
const connected = connect(mapStateToProps, { apiGetRequest,
apiPostRequest })(EditProfile)
const formed = reduxForm({
form: 'editprofile',
})(connected)
In summary: how can I dynamically populate a redux form's initialValues property from a potentially nested object? Is such a thing even practically possible?