Values passed to redux-form initialValues are not rendered - redux-form

I have a problem rendering initialValues passed to a form using redux-form/immutable (v6.0.5) with a custom component:
// WRAPPER COMPONENT
import React from 'react';
import {connect} from 'react-redux';
import DocumentsEditForm from './documentsEditForm';
import {documentsGetOneInfo} from './../../../modules/documents/actions/documents_get_one_info';
import {documentsPut} from './../../../modules/documents/actions/documents_put';
#connect((state) => {
return ({
selectedDocument: state.getIn(['documents', 'selectedDocument']).toJS()
});
})
class DocumentsEdit extends React.Component {
static propTypes = {
dispatch: React.PropTypes.func,
isLoading: React.PropTypes.bool,
routing: React.PropTypes.object,
selectedDocument: React.PropTypes.object
};
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentWillMount() {
this.props.dispatch(adminDocumentsGetOneInfo(this.props.routing.locationBeforeTransitions.query.id));
}
handleSubmit(data) {
this.props.dispatch(documentsPut(data));
}
render() {
const initialValues = (this.props.selectedDocument.id) ? {
filename: this.props.selectedDocument.filename,
id: this.props.selectedDocument.id
} : {};
return (
<DocumentsEditForm
enableReinitialize
initialValues={initialValues}
onSubmitDocumentsEditForm={this.handleSubmit}
/>
);
}
}
export default DocumentsEdit;
Here is the form component itself:
import React from 'react';
import {Field, reduxForm} from 'redux-form/immutable';
import {Button} from 'react-bootstrap';
import InputTextRedux from './../../shared/components/input_text_redux';
import InputSelectRedux from './../../shared/components/input_select_redux';
let DocumentsEditForm = (props) => {
const {error, handleSubmit, pristine} = props;
return (
<form
onSubmit={handleSubmit(props.onSubmitDocumentsEditForm)}
>
<Field
component={InputTextRedux}
disabled
name="id"
type="text"
/>
<Field
component={InputTextRedux}
name="filename"
type="text"
/>
<Button
type="submit"
>
{'Save'}
</Button>
</form>
);
};
DocumentsEditForm.propTypes = {
error: React.PropTypes.object,
handleSubmit: React.PropTypes.func,
onSubmitDocumentsEditForm: React.PropTypes.func,
pristine: React.PropTypes.bool
};
documentsEditForm = reduxForm({
form: 'documentsEditForm'
})(DocumentsEditForm);
export default DocumentsEditForm;
And here is the custom InputTextRedux component:
import React from 'react';
import {ControlLabel, FormControl, FormGroup, InputGroup} from 'react-bootstrap';
const InputTextRedux = (props) => {
const {
id,
disabled,
input,
type
} = props;
return (
<div>
<FormGroup
name={input.name ? input.name : ''}
>
<ControlLabel>
{'Label'}
</ControlLabel>
<InputGroup>
<FormControl
disabled={disabled ? true : false}
id={id ? id : input.name}
onChange={(event) => {
input.onChange(event.target.value);
}}
type={type ? type : 'text'}
value={input.value ? input.value : ''}
/>
<FormControl.Feedback />
</InputGroup>
</FormGroup>
</div>
);
};
InputTextRedux.propTypes = {
disabled: React.PropTypes.bool,
id: React.PropTypes.string,
input: React.PropTypes.object,
type: React.PropTypes.string,
};
export default InputTextRedux;
I can submit the form and get the correct initialValues passed to handleSubmit but I can't seem to get the values displayed.
According to DevTools redux-form/INITIALIZE gets called with the correct payload and also the state at state->form->documentsEditForm->values/initial is correctly updated.
I also tried loading fixed initialValues to rule out problems with the api call delay but I got the same result. "input: {value: ''}" is always an empty string in the Field props.
What I noticed was that inside the props the form component recieves the structure looks as follows:
props {
....
initialValues: {
__altered: false,
_root: {
entries: [
[
'id',
'123456'
],
[
'filename',
'test.txt'
]
]
},
size: 2
},
....
}
If I try to set a field's value directly to "props.initialValues._root.entries[0][1] for example it works.
Also I noticed since the full form has a Field with name="size" that this value gets filled correctly with the value "2" from initialValues above. So it seems as if the form is looking at the wrong "level" of initialValues for the values. To test this I also tried naming a Field "__altered" and its value was correctly set to false.
What am I doing wrong? Thanks alot!

This has been resolved: See https://github.com/erikras/redux-form/issues/1744#issuecomment-251140419
Basically I had to import "redux-form/immutable" instead of "redux-form".

Related

React Redux can't display an array that is a property of an object. What am I doing wrong?

I'm trying to display an object that's passed as props. One of the object properties is an array. The array is seen in the Redux store, is seen on console.log, is seen in the React tools, but when I try to map over it and display it as a list I get TypeError: Cannot read property 'map' of undefined. What am I doing wrong?
I tried to pass the tickets array as a separate prop but I still get the same error. And all the other properties of this.props.event are accessible.
This is my rendering component:
render(){
return(
<div>
{console.log('New EventDetails props event ', this.props.event)}
{console.log('New EventDetails props tickets ', this.props.tickets)}
<h1>Event name: {this.props.event.name}</h1>
<i>{this.props.event.id}</i>
<p>Event description: {this.props.event.description}</p>
<ul><h3>Tickets</h3>
{this.props.event.tickets.map(ticket =>{
return <Link to={`${this.props.event.id}/tickets/${ticket.id}`}><li key={ticket.id}><p>Price: {ticket.price}</p>
<p>Description: {ticket.description}</p>
</li> </Link>
})}
</ul>
</div>
)
}
}
This is the reducer:
import {DISPLAY_EVENT} from '../actions/events'
const eventReducer = (state={}, action) => {
console.log("single event reducer test, actin.payload: ", action.payload) //shows correct payload
switch(action.type) {
case DISPLAY_EVENT:
return action.payload
default:
return state
}
}
I'm passing the props from another component:
import React from 'react'
import {connect} from 'react-redux'
import EventDetails from './EventDetails'
import {getEvent} from '../actions/events'
class EventDetailsContainer extends React.Component {
componentDidMount() {
console.log("Component Did Mount test");
console.log('EventDetailsContainer props:', this.props);
this.props.getEvent(Number(this.props.match.params.id))
}
render() {
return (
<div>
<EventDetails event={this.props.event} tickets={this.props.tickets}/>
</div>
)
}
}
const mapStateToProps = state => {
return {
event: state.event,
tickets:state.event.tickets
}
}
export default connect(mapStateToProps, {getEvent})(EventDetailsContainer)
This is what I get from the console.logs.
I expected that the event.tickets[] will be accessible just as the other properties but instead it gives this error.
you console.log this.props.tickets but then you map over this.props.event.tickets

Text field stays empty

I'm trying to integrate a React-Toolbox Input component with Redux-Form. However, the Input component remains empty when typing. I'm using https://github.com/react-toolbox/react-toolbox/issues/1293 as a guide for the integration.
import React from 'react'
import PropTypes from 'prop-types'
import { Field, reduxForm } from 'redux-form'
import Input from 'react-toolbox/lib/input'
const renderField = ({ input, meta, ...props }) => (
<Input
{ ...input }
{ ...props }
error={ meta.touched && meta.error } />
)
const Form = ({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field
name="myTextField"
component={renderField}
type="text"
/>
</form>
)
Form.propTypes = {
handleSubmit: PropTypes.func.isRequired,
}
export default reduxForm({
form: 'myForm',
})(Form)
This is using react-toolbox 2.0.0-beta.12 and redux-form 7.2.0
You use input, meta and another ...props in your "functional component" renderField, but renderField props argument is named field and is not used anywhere.
You should change renderField this way:
const renderField = ({ input, meta, ...props }) => (
<Input
{ ...input }
{ ...props }
error={ meta.touched && meta.error }
/>
);
UPD
redux-form Basic Usage Guide says:
The redux store should know how to handle actions coming from the form components. To enable this, we need to pass the formReducer to your store. It serves for all of your form components, so you only have to pass it once.
So you should pass formReducer to your store:
import { createStore, combineReducers } from 'redux'
import { reducer as formReducer } from 'redux-form'
const rootReducer = combineReducers({
// ...your other reducers here
// you have to pass formReducer under 'form' key,
// for custom keys look up the docs for 'getFormState'
form: formReducer
})
const store = createStore(rootReducer)

Redux: How to pass store to form created outside the Provider scope

I have written code, which uses a Modal dialog to display a form.
My react app is rendered at "root"
index.html
<div id="root"></div>
App.js
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<ExampleBasic/>
</Provider>
, document.getElementById('root'));
ExmpleBasic.js
Please ignore state management in component here. this is just for example.
import React, { PureComponent } from 'react';
import Lorem from 'react-lorem-component';
import Modal from '#atlaskit/modal-dialog';
import Button from '#atlaskit/button';
export default class ExampleBasic extends PureComponent {
state = { isOpen: false }
open = () => this.setState({ isOpen: true })
close = () => this.setState({ isOpen: false })
secondaryAction = ({ target }) => console.log(target.innerText)
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<BasicFormContainer />
</Modal>
)}
</div>
);
}
}
BasicFormContainer.js
const mapStateToProps = state => ({
addDesignation: state.designations.addDesignation,
});
const mapDispatchToProps = dispatch => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicForm);
BasicForm.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
class BasicForm extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit(values) {
console.log(values);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.submit)}>
<Field
name="designationName"
component="input"
placeholder="Name"
label="Enter name"
autoFocus
/>
</form>
);
}
}
export default reduxForm({
form: 'BasicForm',
enableReinitialize: true,
})(BasicForm);
However modal is rendered using portal, outside current DOM.
As modal is rendered outside the scope of redux context, it is not getting the
store. and i am getting an error "Uncaught Error: Field must be inside a component decorated with reduxForm()"
Below is link to same kind of problem, where redux form within portal is not working.
Redux Form Wrapped Inside Custom Portal Component?
in React 16 it is handled by portals, but version before then that you can try something like as follow.
export default class ExampleBasic extends PureComponent {
...
static contextTypes = { store: React.PropTypes.object };
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<Provider store={this.context.store}>
<BasicFormContainer />
</Provider>
</Modal>
)}
</div>
);
}
}
You need to pass in the values of BasicForm.js to the Redux store and dispatch an action from there itself and not from the BasicFormContainer.js. This way, the Modal remains inside of the scope of your root element and thus there is no need to access the store outside of the Provider.
Then update the Redux store based on the values entered in the form. Once, the store is updated, you can then access it from anywhere in your application such as Modal in your case.
I downgraded to version 2.1.0 to solve the problem.

How to richly style AOR Edit page

Have to create an edit Page editing a number of parameters on an instance of a'tale' resource.
However adding any element such as an MUI Card or even a div, is causing the app to freeze in various ways.
These are the approaches I have tried.
1) Adding a card component or placing my elements within a div for styling
export const EditorEditTale = (props) => {
return (
<Edit {...props} title="Tale Editor">
<SimpleForm >
<div>
<Image />
<TaleCardHeader props={ props } style={taleCardHeaderStyle.editor} />
</div>
</SimpleForm>
</Edit>
)
};
This is causing nothing to render.
Second approach, assuming that the record and basePath arent getting propagated to the children completely. Trying to use component like below.
const Input = ({record, basePath}) => {
return (
<div>
<LongTextInput source="taleText" />
</div>
)
}
This is causing the page to not render with everything in some kind of locking loop with the error - cannot read property touched of undefined.
How should I create a custom Edit page with a complex inputs and styling.
UPDATE: Been trying to write a custom form to substitute the SimpleForm component with no luck so far.
To create a custom form you can follow these steps:
make an exact copy of SimpleForm to your project.
rename SimpleForm to what you want.
fix all the relative imports.
test the new form until it works.
I made a minimum working form based on current master branch's SimpleForm
import React, { Children, Component } from 'react';
import PropTypes from 'prop-types';
import { reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import getDefaultValues from 'admin-on-rest/mui/form/getDefaultValues';
import FormField from 'admin-on-rest/mui/form/FormField';
import Toolbar from 'admin-on-rest/mui/form/Toolbar';
const formStyle = { padding: '0 1em 1em 1em' };
export class PostForm extends Component {
handleSubmitWithRedirect = (redirect = this.props.redirect) => this.props.handleSubmit(values => this.props.save(values, redirect));
render() {
const { children, invalid, record, resource, basePath, submitOnEnter, toolbar } = this.props;
return (
<form className="simple-form">
<Field name="name_of_a_field" component="input" />
{toolbar && React.cloneElement(toolbar, {
handleSubmitWithRedirect: this.handleSubmitWithRedirect,
invalid,
submitOnEnter,
})}
</form>
);
}
}
PostForm.propTypes = {
basePath: PropTypes.string,
children: PropTypes.node,
defaultValue: PropTypes.oneOfType([
PropTypes.object,
PropTypes.func,
]),
handleSubmit: PropTypes.func, // passed by redux-form
invalid: PropTypes.bool,
record: PropTypes.object,
resource: PropTypes.string,
redirect: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
]),
save: PropTypes.func, // the handler defined in the parent, which triggers the REST submission
submitOnEnter: PropTypes.bool,
toolbar: PropTypes.element,
validate: PropTypes.func,
};
PostForm.defaultProps = {
submitOnEnter: true,
toolbar: <Toolbar />,
};
const enhance = compose(
connect((state, props) => ({
initialValues: getDefaultValues(state, props),
})),
reduxForm({
form: 'record-form',
enableReinitialize: true,
}),
);
export default enhance(PostForm);
The above code works for AOR's example.
I hope this helps.
(import might be slightly different when you have AOR as npm dependency :
import getDefaultValues from 'admin-on-rest/lib/mui/form/getDefaultValues';
import FormField from 'admin-on-rest/lib/mui/form/FormField';
import Toolbar from 'admin-on-rest/lib/mui/form/Toolbar';
)
Documenting my final answer. You have to create a custom Redux Form. You can use AOR Input components straight. They come prewrapped for Redux Form.
import { Field, reduxForm } from 'redux-form';
import compose from 'recompose/compose';
import { connect } from 'react-redux';
class StyledForm extends Component {
// Newer version of aor needs this function defined and passed to save buttons. All props are being passed by parent List component.
handleSubmitWithRedirect = (redirect = this.props.redirect) => this.props.handleSubmit(values => this.props.save(values, redirect));
render() {
const { handleSubmit, invalid, record, resource, basePath } = this.props
return (<div>
<form onSubmit={handleSubmit} >
<Card >
<CardText >
//This component simply displays data, something not possible very easily with SimpleForm.
<HeaderComp basePath={basePath} record={record} />
<Field source="category_id"
optionText="categoryName"
reference="categories"
resource={resource}
record={record}
basePath={basePath}
name="NAME OF THE FIELD IN YOUR REDUX DATASTORE"
component={REFERENCEFIELDCOMP} />
//create complex div structures now.
<div >
<span>Tale</span>
<Field resource={resource} record={record} basePath={basePath} name="taleText" component={TextInput} />
</div>
</CardText >
<MuiToolbar>
<ToolbarGroup>
<SaveButton handleSubmitWithRedirect={this.handleSubmitWithRedirect}/>
//Add custom buttons with custom actions
<Field record={record} name="status" component={EditButtons} />
</ToolbarGroup>
</MuiToolbar>
</Card>
</form>
</div>)
}
};
const enhance = compose(
connect((state, props) => ({
initialValues: getDefaultValues(state, props),
})),
reduxForm({
form: 'record-form',
enableReinitialize: true,
}),
);
export default enhance(StyledForm);
You will have to either import or copy getDefaultValues from AOR in the node modules.
I copied it into the file below.
import getDefaultValues from '../functions/getDefaultValues';
If you need a referenceField in your field. Then wrap it in a custom component like shown below
const DropDownSelector = ({optionText, ...props}) => {
return (
<ReferenceInput {...props} label="" >
<SelectInput optionText={optionText} />
</ReferenceInput>
)
}

Fields not being passed correctly to Props using ReduxForm

I'm using Redux Form in one of my projects (pretty much just copying the dynamic one from Rally Coding), but whenever I access this.props.fields, it simply gives me an array of the names of my fields as opposed to an object. What's even weirder is that I'm copying and pasting this code into another one of my projects that uses RF and it's giving me what I want from this.props.fields. Part of me thinks that I set RF up incorrectly, but I did import the formReducer into App.js and combined it with my other reducers.
When I hit the debugger, this.props.fields = ['query', 'numberOfResults'] which is messing everything up.
Here's my code:
import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import { Field, reduxForm } from 'redux-form';
const FIELDS = {
query: {
type: 'input',
label: 'What are you looking for?'
},
numberOfResults: {
type: 'input',
label: 'Number of Results'
}
};
class YelpForm extends Component {
onSubmit(props) {
console.log('hey cutie')
}
renderField(fieldConfig, field) {
debugger
const fieldHelper = this.props.fields[field]
return (
<div className={`form-group ${fieldHelper.touched && fieldHelper.invalid ? 'has-danger' : '' }`} >
<label>{fieldConfig.label}</label>
<fieldConfig.type type="text" className="form-control" {...fieldHelper} />
<div className="text-help">
{fieldHelper.touched ? fieldHelper.error : ''}
</div>
</div>
);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(props => this.onSubmit(props))} >
{_.map(FIELDS, this.renderField.bind(this))}
<input type="submit">Submit</input>
</form>
);
}
}
function validate(values) {
const errors = {};
_.each(FIELDS, (type, field) => {
if (!values[field]) {
errors[field] = `Enter a ${field}`;
}
});
return errors;
}
export default reduxForm({
form: 'Yelp Form',
fields: _.keys(FIELDS),
validate
})(YelpForm);
This is my first question on StackOverflow; thanks for the help in advance!
Try downgrading to redux-form version 5.2.3. It seems version 6.0.2 is either buggy, or not documented correctly.

Resources