Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling form defaults #176

Closed
mattiamanzati opened this issue Oct 27, 2015 · 16 comments
Closed

Handling form defaults #176

mattiamanzati opened this issue Oct 27, 2015 · 16 comments
Labels
enhancement improvements in current API

Comments

@mattiamanzati
Copy link

Hi all, thanks again for this awesome library, and thanks for the v3 rewrite, I was waiting for that! :D

I use redux-form since 3 months ago, and the only thing I am not too comfortable with is the way of defaults value is handled. Maybe I am missing something, and I know (and I am actually doing it) this could be solved by an higher component of the form component, but I think that this problem should be addressed by the reduxForm() function.

Here's an example.
I have a custom component which displays a select with options (of cars for example) picked from the state, and by default the form should assume as value the first one available.
Assuming that the cars picker components is a controller component with value and onChange, is almost impossible to make, by default, the first car of the state.cars selected.

Here's my purposal.
Why not provide some sort of mapStateToDefaultFormValue?

reduxForm({
   ...
   defaults: (state, props, fields) => fields.reduce((prev, curr) => prev[curr] = '', {})
   ...
})

The function takes in the state (as connect's mapStateToProps does), the form component props and, as extra that could be usefull, a copy of the current field confing that could be used, for example, to set the default value to all fields.

The return of this defaults, is then merged with initialValues provided.

This way initial values contains only the actual form data stored in the db, and this enables things like

for forms with pickers.

@afitiskin
Copy link
Contributor

As I understand, the redux-way to achieve your goal is following:

  1. Add plugin to your form reducer:
import {createStore, combineReducers} from 'redux';
import {reducer as formReducer} from 'redux-form';
import defaultValuePlugin from './plugins/default-value';

const reducers = {
  // ... your other reducers here ...
  form: formReducer.plugin({
    formWithDefaultValue: defaultValuePlugin
  })
}
const reducer = combineReducers(reducers);
const store = createStore(reducer);
  1. When your data is loaded (e.g. list of cars), some action should be dispatched (e.g. action with type CAR_LIST_LOADED). So you have to handle this action in your plugin and change form state:
export default function defaultValuePlugin(state, action) {
  switch (action.type) {
    case 'CAR_LIST_LOADED':
      const firstCarId = getFirstCarId(action.data);
      return {
        ...state,
        carSelector: {
          value: firstCarId,
        },
      };
    default:
      return state;
  }
}
  1. In form component you don't need to change anything, just add select element (in my example called carSelector) to form, that's it.

So, everytime when you need to change form value (to set defaults, to update values, etc.) you dispatch specific action, and handle it in your plugin.
@erikras, am I correct?

@mattiamanzati
Copy link
Author

Yeah, that's an alternative solution to the higher component, but the question is: Should this be really done like this? Should'nt forms defaults stick together with form definition?

Also, in your example you listen to car list loaded, it should listen for form initialization (and that's why time ago I requested for actionTypes export ;) and that makes the thinks a bit trickier

@afitiskin
Copy link
Contributor

Also, in your example you listen to car list loaded, it should listen for form initialization (and that's why time ago I requested for actionTypes export ;) and that makes the thinks a bit trickier

I think it should listen both actions (internal form initialise action and car list load action), because you can't guarantee that car list is loaded before form initialisation :)
I agree with you, this way is a bit trickier. I'm also interested to find a simple and robust way to set form defaults. And I prefer to go redux-way, to use reducers to manipulate data in store.

@x1a0
Copy link

x1a0 commented Oct 31, 2015

I have same question about default values. In my case it's an account profile form which will have user's current profile values as default values, and user's data is in user state.

I was thinking about using @connect to bind user state to the decorated form component, but still not sure how to set default values.

UPDATE: actually I found initialValues is designed for that 👍

@erikras erikras added the enhancement improvements in current API label Nov 2, 2015
@erikras
Copy link
Member

erikras commented Nov 16, 2015

@mattiamanzati I just re-read this issue, and I have a question. Already you can use mapStateToProps in reduxForm() to pull Redux state into the initialValues prop. But it sounds like what you're asking for is the merging of those values with values provided elsewhere (perhaps via a prop)? Or do you mean a separate defaultValues that should be used when any of the fields that match a key in defaultValues is undefined?

@mattiamanzati
Copy link
Author

I am talking about a set of default values that should be merged with initialValues.
At the time that I written this I did'nt know (or maybe was'nt available yet) mapStateToProps, but the basic idea is to have a "defaults" setting in the reduxForm() which is used as value whenever an initialValue is not provided or the user have'nt touched the input.
So yes, it could be considered as "merging" initialValues.

@wmertens
Copy link
Contributor

I don't understand initialValues vs defaultValues.

IMHO the only sane behavior is that the initialValues prop directly maps to the default values of fields, and when it changes, the change in defaults is dispatched.

It is presumably always safe to change defaults, if there are conflicts possible between set and unset fields that is uu to the programmer to handle…

@flyfy1
Copy link

flyfy1 commented Dec 4, 2015

I don't understand initialValues vs defaultValues either.. Is there any example showing how to use these properties? Especially for customised components.

Additionally, for my customized component, I tried doing initialization in the constructor via onChange call, like below:

export default class extends React.Component{
  constructor(props){
    super(props);
    this.state = {hoverAt: null};

    this.props.onChange(5)
  }
}

I feel it is wrong cuz it's relaying upon onChange to set the initial value... I should use some kind of initialValue property instead. Any comments on this approach?

@madshargreave
Copy link

Any solution to this?

@erikras
Copy link
Member

erikras commented Jan 26, 2016

@madshargreave Well, there's the Initialize From State example that was more or less a response to this issue.

And there is also an active discussion going on in #578 about modifying the decision in #370.

@erikras
Copy link
Member

erikras commented Apr 25, 2016

FYI, v5.1.4 might help with this.

@wmertens
Copy link
Contributor

wmertens commented Aug 3, 2016 via email

@tannerlinsley
Copy link

Honestly I ended up wrapping the entire form component in a simple
component that extends the initial Values prop using object assign and my
defaults.
On Wed, Aug 3, 2016 at 6:16 AM Wout Mertens notifications@github.com
wrote:

Would this work: <InputField value={defaultVal} {...field}/>? If value is
defined in field it will overwrite the default.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#176 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AFUmCfYhvjSCeZquIklAMq4ZE55KW2yOks5qcH4vgaJpZM4GWRGz
.

@wmertens
Copy link
Contributor

wmertens commented Aug 3, 2016 via email

@luigiplr
Copy link

Just incase anyone else finds this issue and has issues this is how I solved it:

@connect(({ user: { profile: { first, middle, last, email } } }) => {
  const fullName = joinNameParts(first, middle, last)
  return { initialValues: { fullName, email } }
})
@reduxForm({ form: 'edit-profile' })

Where my form had fields fullName & email respectively.

@lock
Copy link

lock bot commented Jun 2, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 2, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement improvements in current API
Projects
None yet
Development

No branches or pull requests

9 participants