I am struggling to get the value out of the Material-ui Autocomplete when using redux-form. Has anyone solved this? I am using the exact example from the material-ui Autocomplete https://material-ui.com/components/autocomplete/ I am able to see the list options and it populates after clicking it twice, but I am unable to extract the real value, instead I am returning ({ title : 0 }) instead of the value.
import React from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { Field, reduxForm } from "redux-form";
import { connect } from "react-redux";
class Form extends React.Component {
onSubmit = formValues => {
console.log(formValues);
};
renderTextField = ({
label,
input,
meta: { touched, invalid, error },
...custom
}) => (
<Autocomplete
label={label}
options={this.props.movies}
placeholder={label}
getOptionLabel={option => option.title}
onChange={input.onChange}
{...input}
{...custom}
renderInput={params => (
<TextField {...params} label={label} variant="outlined" fullWidth />
)}
/>
);
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
name="propertySelector"
component={this.renderTextField}
label="Select Property"
type="text"
/>
</form>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state);
return {
movies: [
{ title: "The Shawshank Redemption", year: 1994 },
{ title: "The Godfather", year: 1972 },
{ title: "Schindler's List", year: 1993 }
]
};
};
Form = reduxForm({
form: "auto_complete"
});
export default connect(mapStateToProps, null)(Form);
Solved by passing in the (event, value) to the onChange props.
onChange={(event, value) => console.log(value)}
From the docs;
Callback fired when the value changes.
Signature:
function(event: object, value: T) => void
event: The event source of the callback.
value: null
Related
I'm making a todo app and using useState to pass value to the form then submit the todo but for some reasons my todo form is not render and i don't know what is missing in my codes, please help me to check! Thank you so much!
import React, { useState } from "react";
function Todo({ todo, index }) {
console.log("hiiii");
return (
<div>
<p>{todo.text}</p>
</div>
);
}
function todoForm(addTodo) {
const [value, setValue] = useState("");
handleSubmit = (e) => {
e.preventDefault();
if (!value) return;
addTodo(value);
setValue("");
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="add new todo"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
</form>
</div>
);
}
function App() {
const [todos, setTodos] = useState([
{
text: "eat lunch",
isCompleted: false
},
{
text: "do homework",
isCompleted: false
},
{
text: "go to school",
isCompleted: false
}
]);
addTodo = (text) => {
console.log("hey");
const newTodos = [...todos, { text }];
setTodos(newTodos);
};
return (
<div>
<div>
{todos.map((todo, index) => {
return <Todo key={index} index={index} todo={todo} />;
})}
</div>
<div>
<todoForm addTodo={addTodo} />
</div>
</div>
);
}
export default App;
Link sandbox: https://codesandbox.io/s/serverless-bash-ef4hk?file=/src/App.js
JSX tags must be uppercased in order to be properly parsed by the compiler as a React component.
Instead of todoForm, use TodoForm.
Capitalized types indicate that the JSX tag is referring to a React component. These tags get compiled into a direct reference to the named variable, so if you use the JSX expression, Foo must be in scope.
From: https://reactjs.org/docs/jsx-in-depth.html#specifying-the-react-element-type
Also, you need to destructure props inside TodoForm in order to gain access to addTodo:
// Bad
function TodoForm(addTodo) {...}
// Good
function TodoForm({addTodo}) {...}
You should also assign you handlers to consts:
// Bad
addTodo = (text) => {...};
// Good
const addTodo = (text) => {...};
your problem is solved it
APP.JS
import React, { useState } from "react";
function Todo({ todo, index }) {
console.log("hiiii");
return (
<div>
<p>{todo.text}</p>
</div>
);
}
function todoForm(addTodo) {
const [value, setValue] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (!value) return;
addTodo(value);
setValue("");
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="add new todo"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
</form>
</div>
);
}
function App() {
const [todos, setTodos] = useState([
{
text: "eat lunch",
isCompleted: false
},
{
text: "do homework",
isCompleted: false
},
{
text: "go to school",
isCompleted: false
}
]);
const addTodo = (text) => {
console.log("hey");
const newTodos = [...todos, { text }];
setTodos(newTodos);
};
return (
<div>
<div>
{todos.map((todo, index) => {
return <Todo key={index} index={index} todo={todo} />;
})}
</div>
<div>
{todoForm(addTodo)}
</div>
</div>
);
}
export default App;
I have added the material-ui/Checkbox component in admin-on-rest create form with source attribute. But after I click save button, I could not see the checkbox value in posted data.
But I can see 'title' and 'body' fields value in posted data. Can someone please tell, Why this code is not working?
Here is my sample code:
export const PostCreate = (props) => (
<Create {...props} >
<SimpleForm>
<TextInput source="title" />
<LongTextInput source="body" />
<Checkbox
label="Label on the left"
labelPosition="left"
source="test"
value="yes"
/>
</SimpleForm></Create>
);
Sure Checkbox is not a react-on-admin component. Please use BooleanInput
The BooleanInput.js module from rect-admin, Switch is replaced with Checkbox:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import FormControlLabel from '#material-ui/core/FormControlLabel'
import FormGroup from '#material-ui/core/FormGroup'
import Checkbox from '#material-ui/core/Checkbox'
import { addField, FieldTitle } from 'ra-core'
const sanitizeRestProps = ({
alwaysOn,
basePath,
component,
defaultValue,
formClassName,
initializeForm,
input,
isRequired,
label,
locale,
meta,
options,
optionText,
optionValue,
record,
resource,
allowEmpty,
source,
textAlign,
translate,
translateChoice,
...rest
}) => rest
export class CheckboxInput extends Component {
handleChange = (event, value) => {
this.props.input.onChange(value)
}
render() {
const {
className,
input,
isRequired,
label,
source,
resource,
options,
...rest
} = this.props
return (
<FormGroup className={className} {...sanitizeRestProps(rest)}>
<FormControlLabel
control={
<Checkbox
color="primary"
checked={!!input.value}
onChange={this.handleChange}
{...options}
/>
}
label={
<FieldTitle
label={label}
source={source}
resource={resource}
isRequired={isRequired}
/>
}
/>
</FormGroup>
)
}
}
CheckboxInput.propTypes = {
className: PropTypes.string,
input: PropTypes.object,
isRequired: PropTypes.bool,
label: PropTypes.string,
resource: PropTypes.string,
source: PropTypes.string,
options: PropTypes.object,
}
CheckboxInput.defaultProps = {
options: {},
}
export default addField(CheckboxInput)
I am validating a input field ( required , min length 3 ) with VeeValidate plugin.. it's working fine
but how I can avoid the onInput action to be called ( to avoid commit in store when the input becomes invalid ( as soon as input aria-invalid switch to false )
shortly said : Is there anyway to switch calling/not Calling onInput: 'changeTitle' when the input field aria-invalid is false/true ?
thanks for feedback
ChangeTitleComponent.vue
<template>
<div>
<em>Change the title of your shopping list here</em>
<input name="title" data-vv-delay="1000" v-validate="'required|min:3'" :class="{'input': true, 'is-danger': errors.has('required') }" :value="title" #input="onInput({ title: $event.target.value, id: id })"/>
<p v-show="errors.has('title')">{{ errors.first('title') }}</p>
</div>
</template>
<style scoped>
</style>
<script>
import { mapActions } from 'vuex'
import Vue from 'vue'
import VeeValidate from 'vee-validate'
Vue.use(VeeValidate)
export default {
props: ['title', 'id'],
methods: mapActions({ // dispatching actions in components
onInput: 'changeTitle'
})
}
</script>
vuex/actions.js
import * as types from './mutation_types'
import api from '../api'
import getters from './getters'
export default {
...
changeTitle: (store, data) => {
store.commit(types.CHANGE_TITLE, data)
store.dispatch('updateList', data.id)
},
updateList: (store, id) => {
let shoppingList = getters.getListById(store.state, id)
return api.updateShoppingList(shoppingList)
.then(response => {
return response
})
.catch(error => {
throw error
})
},
...
}
UPDATE
I tried to capture the input value with #input="testValidation) and check for a valid input value (required)
if valid ( aria-invalid: false) then I emit the input value, but the props are not updated in the parent component and the vuex action 'changeTitle' is not triggered
<template>
<div>
<em>Change the title of your shopping list here</em>
<input name="title" ref="inputTitle" data-vv-delay="1000" v-validate="'required'" :class="{'input': true, 'is-danger': errors.has('required') }" :value="title" #input="testValidation({ title: $event.target.value, id: id })"/>
<p v-show="errors.has('title')">{{ errors.first('title') }}</p>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import Vue from 'vue'
import VeeValidate from 'vee-validate'
Vue.use(VeeValidate)
export default {
props: ['title', 'id'],
methods: {
testValidation: function (value) {
const ariaInvalid = this.$refs.inputTitle.getAttribute('aria-invalid')
if (ariaInvalid === 'false') {
this.$nextTick(() => {
this.$emit('input', value) // should change props in parent components
})
} else {
console.log('INVALID !') // do not change props
}
},
...mapActions({
onInput: 'changeTitle' // update store
})
}
}
</script>
like you access the errors collection in the VUE template, you can also access the same errors collection in your testValidation method
so replace
const ariaInvalid = this.$refs.inputTitle.getAttribute('aria-invalid')
with
const ariaInvalid = this.$validator.errors.has('title')
grtz
I got an issue may cause from react-boilerplate Or "redux-form/immutable", wish someone can help me out.
I tried to put some custom props into Form component and this will print out messages of error when submit.
Here is my code:
import React from 'react';
import { Form, Icon } from 'semantic-ui-react';
import { PropTypes } from 'prop-types';
import { Field, reduxForm, reset } from 'redux-form/immutable';
import { connect } from 'react-redux';
import { ReduxFormInput, ReduxFormCheckbox } from '../../components/ReduxFormInput';
import { signupSync, passStrength } from '../../components/Validate';
import StyledButton from '../../components/StyledButton';
import AcceptTerms from './acceptTerms';
import signupRequest from './actions';
class Signup extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
signupRequest: PropTypes.func,
signup: PropTypes.shape({
requesting: PropTypes.bool,
successful: PropTypes.bool,
messages: PropTypes.array,
errors: PropTypes.array,
}),
}
submit(values, dispatch) {
console.log(values);
this.props.signupRequest(values); // will be undefined 'props' after submit
}
render() {
const {
handleSubmit,
submitting,
signup: {
requesting,
successful,
messages,
errors,
},
} = this.props;
return (
<Form onSubmit={handleSubmit(this.submit)} >
<Form.Field>
<Field
type="text"
name="accountName"
component={ReduxFormInput}
icon="user outline"
label="Tên tài khoản"
placeholder="Tên tài khoản của bạn"
/>
</Form.Field>
<Form.Field >
<Field
type="email"
name="email"
component={ReduxFormInput}
icon="mail outline"
label="Email"
placeholder="Email của bạn"
/>
</Form.Field>
<Form.Field required >
<Field
type="password"
name="password"
component={ReduxFormInput}
icon="lock"
label="Mật khẩu"
placeholder="Nhập mật khẩu"
warn={passStrength}
/>
</Form.Field>
<Form.Field required >
<Field
type="password"
name="confirmPassword"
component={ReduxFormInput}
icon="lock"
label="Xác nhận Mật khẩu"
placeholder="Xác nhận lại mật khẩu"
/>
</Form.Field>
<Form.Field>
<Field
defaultChecked
type="checkbox"
name="confirm"
checkboxLabel="Tôi muốn nhận thông tin thông qua email, SMS, hoặc điện thoại."
component={ReduxFormCheckbox}
/>
</Form.Field>
{AcceptTerms}
<div>
<StyledButton primary fluid type="submit" disabled={submitting} >
<Icon name="add user" />
Đăng ký tài khoản
</StyledButton>
</div>
</Form>
);
}
}
const mapStateToProps = (state) => ({
signup: state.signup,
});
const connected = connect(mapStateToProps, { signupRequest })(Signup);
const formed = reduxForm({
form: 'signup',
validate: signupSync,
onSubmitSuccess: afterSubmit,
})(connected);
const afterSubmit = (result, dispatch) => dispatch(reset('signup'));
export default formed;
My reducer
import { SubmissionError } from 'redux-form/immutable';
import {
SIGNUP_REQUESTING,
SIGNUP_SUCCESS,
SIGNUP_ERROR,
} from './constants';
const initialState = {
requesting: false,
successful: false,
errors: [],
messages: [],
};
const reducer = function signupReducer(state = initialState, action) {
switch (action.type) {
case SIGNUP_REQUESTING:
return {
requesting: true,
successful: false,
errors: [],
messages: [{
body: 'Signing up...',
time: new Date(),
}],
};
case SIGNUP_SUCCESS:
return {
requesting: false,
successful: true,
errors: [],
messages: [{
body: `Successfully created account for ${action.response.email}`,
time: new Date(),
}],
};
case SIGNUP_ERROR:
return {
requesting: false,
successful: false,
messages: [],
errors: new SubmissionError({
email: 'failed',
_error: 'failed',
}),
};
default:
return state;
}
};
export default reducer;
Inject reducer on routes.js
...
{
path: '/signup',
name: 'signup',
getComponent(nextState, cb) {
const importModules = Promise.all([
import('containers/SignupPage/reducer'),
import('containers/SignupPage/sagas'),
import('containers/SignupPage'),
]);
const renderRoute = loadModule(cb);
importModules.then(([reducer, sagas, component]) => {
injectReducer('signup', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
});
},
}
...
Then I got an error screen like this.
TypeError: Cannot read property 'requesting' of undefined
Signup.render
/Users/son/Desktop/we-mak/app/containers/SignupPage/signupForm.js
Signup.tryRender
http://localhost:3000/main.js:1388:1
Signup.proxiedMethod
http://localhost:3000/main.js:1356:1
eval
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:796:21
measureLifeCyclePerf
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:75:12
ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:795:25
ReactCompositeComponentWrapper._renderValidatedComponent
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:822:32
ReactCompositeComponentWrapper._updateRenderedComponent
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:746:36
ReactCompositeComponentWrapper._performComponentUpdate
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:724:10
ReactCompositeComponentWrapper.updateComponent
webpack:///./~/react-dom/lib/ReactCompositeComponent.js?:645:12
I notice that react-boilerplate doesn't use react-hot-loader so I guessed it may cause from boilerplate, but I don't have enough webpack experience to config it.
This error message means your signup property is undefined, which may happen if your state does not have signup property or that property is undefined. Have a look at your reducer.
Trying to get Calendar to work with a Redux Form
Using a Redux Form Field:
<Field name={name} component={this.renderCal}/>
which is leveraging a stateless function:
renderCal({input, ...rest}) {
input.value = new Date();
return <Calendar {...input}
onChange={() => input.onChange(input.value)}
value={input.value}
{...rest}/>
}
When I submit the form, the value is still null. I appears like the value is not bound to the component. This is my request payload from the Chrome Developer Tools > Network ...
inputs : [{name: "fromDate", title: "From Date", dataType: "date", format: "mm/dd/yyyy", value: null}] 0 : {name: "fromDate", title: "From Date", dataType: "date", format: "mm/dd/yyyy", value: null} dataType : "date" format : "mm/dd/yyyy" name : "fromDate" title : "From Date" value : null
Does anyone have Calendar working with Redux Form as a stateless function?
Thanks,
Steve
Steve and I found a solution. Posting back our findings for everyone else...
To clarify, the initial state/shape of the form is actually an array of dates:
{
myDates: [
{myDate: null},
{myDate: null},
{myDate: null}
]
}
If you just need to tie DateTimePicker up to a Redux Form field then you can drop renderDynamicFields and use renderDynamicField directly:
<Field name={name} component={renderDynamicField}/>
Points of Note:
Changed directions slightly and are now using DateTimePicker as opposed to Calendar because we also needed the time portion. That being said the solution should work with Calendar too.
Used Redux Form FieldArray
DateTimePicker needed a "defaultValue"
Solution
import React from "react";
import {Button, Col, Form, FormGroup, ControlLabel} from "react-bootstrap";
import {reduxForm, Field, FieldArray} from "redux-form";
import {connect} from "react-redux";
import {DateTimePicker} from "react-widgets";
class ReactWidgets extends React.Component {
constructor(props) {
super(props);
this.renderDynamicFields = this.renderDynamicFields.bind(this);
}
submit(form) {
console.log('form', form);
}
renderDynamicFields({fields = []}) {
let collection = [];
fields.map((name, index) => {
collection.push(<Field key={index} name={`${name}.myDate`} component={renderDynamicField}/>);
});
return <div>{collection}</div>;
function renderDynamicField(props) {
const {input} = props;
let component = null;
if (true) { // going to support different types...
component = <DateTimePicker
defaultValue={input.value || new Date()}
onChange={(value) => {
input.onChange(value);
}}/>;
}
return component;
}
}
render() {
const {error, handleSubmit, pristine, reset, submitting} = this.props;
return (
<Form horizontal onSubmit={handleSubmit(this.submit)}>
<FormGroup><Col componentClass={ControlLabel} xs={2}>
My Date2</Col>
<Col xs={4}>
<FieldArray name="myDates" component={this.renderDynamicFields}/>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={1} xs={11}>
<Button type="submit" bsStyle="primary"
disabled={pristine || submitting}>Save</Button>
<Button type="button" disabled={pristine || submitting}
onClick={reset}>Reset</Button>
</Col>
</FormGroup>
</Form>
);
}
}
ReactWidgets = reduxForm({
form: 'reactWidgets'
})(ReactWidgets);
ReactWidgets = connect(
state => {
return {
initialValues: {
myDates: [
{myDate: null},
{myDate: null},
{myDate: null}
]
},
}
})(ReactWidgets)
export default ReactWidgets