i am using redux forms and redux to push form values into the store/state. First thing is, i get the state as undefined in form component when i pass it as props from container(INFORMATION COMPONENT) component which is not a React component class and finally use update function to update the store. I am new to React and the redux form is throwing me off
// App Component
class App extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div className="App">
<InformationFrom state={this.props}/>
</div>
);
}
}
const mapStateToProps = (reduxState) => {
return reduxState;
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators(actionCreators, dispatch);
}
export default connect(
mapStateToProps, mapDispatchToProps
)(App)
// InformationForm Component
export default class InformationFrom extends React.Component {
render() {
return (
<div>
{console.log("infoForm props >> ", this.props)}
<SimpleForm/>
</div>
)
}
}
// Form stateless function component
const SimpleForm = props => {
const { handleSubmit, pristine, reset, submitting } = props
const state = this.props.state; // is undefined
return (
<div>
{ console.log("from state here >> ", this.props) }
<form onSubmit={handleSubmit(this.updateStore(values,state))} name="InformForm">
<div>
<div>
<Field name="firstName" component={renderInput} type="text" placeholder="First Name" label={"First Name"} />
<Field name="lastName" component={renderInput} type="text" placeholder="Last Name" label={"Last Name"} />
<Field name="email" component={renderInput} type="text" placeholder="Email" label={"Email"} />
</div>
</div>
<div style={style.normal}>
<button type="submit" disabled={submitting}>
Submit
</button>
<button type="button" disabled={submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
</div>
)
}
function updateStore(values, state){
let newStore = {};
newStore = {
}
return newStore;
}
const validate = values => {
const errors = {}
if (!values.firstName) {
errors.firstName = "Required!"
}
return errors;
}
const style = {
normal: {
margin: '10px'
}
}
export default reduxForm({
form: 'simple', // a unique identifier for this form
destroyOnUnmount: false,
validate
})(SimpleForm)
You are not pass props to SimpleForm in InformationFrom, so need to update
export default class InformationFrom extends React.Component {
render() {
return (
<div>
{console.log("infoForm props >> ", this.props)}
<SimpleForm/>
</div>
)
}
}
to
<SimpleForm {...this.props} />
and Export with connect to get state from redux
export default connect((state)=>state)( reduxForm({
form: 'simple', // a unique identifier for this form
destroyOnUnmount: false,
validate
})(SimpleForm) );
Related
I am learning Redux and trying to Add/View users using redux in my react app.By using 'ref' in react, I am reading the payload(name,account_number) of new user and passing to 'addProfile' action creator onClick of 'Add' button in this way -
AddView.js
import React from 'react';
import { connect } from 'react-redux';
import { addProfile } from '../actions';
class AddView extends React.Component{
addValues(){
return(
<div>
Name : <input type="text" value={this.props.profiles.name} ref={el => this.nameValue=el}/>
Account Number : <input type="text" value={this.props.profiles.account_number} ref={el => this.accountValue=el}/>
<button onClick={() => this.props.addProfile(this.nameValue,this.accountValue)}>Add</button>
</div>
);
}
render(){
return (
<div>
Add Profile
<br /><br />
{this.addValues()}
</div>
);
}
}
const mapStateToProps = (state) => {
return { profiles : state.profiles }
}
export default connect(mapStateToProps, {addProfile}) (AddView);
Now am trying to console log the name,account_number in my action creator but I get html instead of values.
export const addProfile = (name, account_number) => {
console.log(name,account_number)
return{
type :'ADD_PROFILE',
payload : {
name : name,
account_number : account_number
}
};
}
Can anyone please help where I went wrong. Full code here - https://codesandbox.io/s/239j97y36p
React refs give you a ref to the dom element, if you just want the value of the input you can get it with .value. I would also rename your ref variables then to be accurate like nameInputRef and accountInputRef.
Name :{" "}
<input
type="text"
value={this.props.profiles.name}
ref={el => (this.nameInputRef = el)}
/>
Account Number :{" "}
<input
type="text"
value={this.props.profiles.account_number}
ref={el => (this.accountInputRef = el)}
/>
<button
onClick={() =>
this.props.addProfile(this.nameInputRef.value, this.accountNumberRef.value)
}
> Add
</button>
You can see full sample adapted from yours here: https://codesandbox.io/s/k3mp28lr3o
class UserProfile extends Component {
constructor(props) {}
render() {
// ref={el => this.nameValue=el} to access input variable
// or
// use onChange event which fire another dispatcher which mutate profile state since we assign input values to profile state you can use state to get latest values
//this.props.profiles
//this.props.onAddProfile()
}
}
const mapStateToProps = state => {
return {
profiles : state.profiles
}
}
const mapDispatchToProps = dispatch => {
return {
onAddProfile:dispatch(addProfile())
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserProfile);
You are assigning the element into the variable during the onclick event.
ref={el => this.nameValue=el}
You can use a local state to store the value of the while onChange instead of ref
npm install redux-thunk --save
profile.jsx
class Profile extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<input
type="text"
onChange={({ target }) =>
this.props.onUsernameUpdated(target.value)
}
value={this.props.profile.username}
placeholder="Username"
/>
<input
type="text"
onChange={({ target }) =>
this.props.onAccNumberUpdated(target.value)
}
value={this.props.profile.accNumber}
placeholder="Account Number"
/>
<button onclick={this.props.onUpdateProfile}>Submit</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
profile: state.profile
};
};
const mapDispatchToProps = dispatch => {
return {
onUsernameUpdated: username => dispatch(usernameUpdated(username)),
onAccNumberUpdated: password => dispatch(accNumberUpdated(accNumber)),
onUpdateProfile: () => dispatch(updateProfile())
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Profile);
actions.js
export function usernameUpdated(username) { // need reducer to update state profile name
return {
type: 'USER_NAME_UPDATE',
data: username
}
}
export function accNumberUpdated(accNumber) { // need reducer to update state profile acc number
return {
type: 'ACC_NUM_UPDATE',
data: accNumber
}
}
export function updateProfile() {
return (dispatch, getState) => { //thunk magic
// latest profile after user input
const profile = getState().profile
}
}
Im struggling to find a solution when testing a redux-form component. The problem is that only when I test simply if the component is rendering it gives me an error: "TypeError: handleSubmit is not a function", but the app is working fine, as expected.
I've tried to solve it just to make handleSubmit a function and not taking it from props, but then the app doesn't work. When the submit form is correct it must navigate to /landing page, but instead just re-render the login component.
The component:
import React, { Component } from 'react'
//import { Link } from 'react-router-dom'
import { Field, reduxForm } from 'redux-form'
import '../../style/style.css'
export class Login extends Component {
renderField(field) {
const { meta: { touched, error} } = field;
const className = `form-group ${touched && error ? 'has-danger' : ''}`;
return (
<div className={className}>
<input className="form-control" id="username_field" placeholder={field.label} type="text" {...field.input} />
<div className="text-help">
{ field.meta.touched ? field.meta.error : '' }
</div>
</div>
)
}
renderPasswordField(field) {
const { meta: { touched, error} } = field;
const className = `form-group ${touched && error ? 'has-danger' : ''}`;
return (
<div className={className}>
<input className="form-control" id="password_field" placeholder={field.label} type="password" {...field.input} />
<div className="text-help">
{ field.meta.touched ? field.meta.error : '' }
</div>
</div>
)
}
onSubmit(values) {
this.props.history.push('/landing')
}
// DOESN'T WORK!!!
// handleSubmit(formValues){
// //console.log(formValues);
// }
render() {
const { handleSubmit } = this.props
return (
<div>
<div className="login-form">
<form onSubmit={ /*this.*/handleSubmit(this.onSubmit.bind(this))}>
<h2 className="text-center">TQI Log in</h2>
<div className="form-group">
<Field id="username" name="username" label="username" component={this.renderField} />
</div>
<div className="form-group">
<Field id="password" name="password" label="password" component={this.renderPasswordField} />
</div>
<div className="form-group">
<button id="login_button" type="submit" className="btn btn-primary btn-block">Login </button>
</div>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {}
const dummyData = {
username: 'admin',
password: '123'
}
// Validate the inputs from values
if(!values.username) {
errors.username = "Enter a username"
} else if(values.username !== dummyData.username){
errors.username = "Wrong username"
}
if(!values.password) {
errors.password = "Enter a password"
} else if( values.username === dummyData.username && values.password !== dummyData.password){
errors.password = "Wrong password"
}
// if errors is empty, the form is fine to submit
// If errors has *any* properties, redux forms assumes form is invalid
return errors
}
export default reduxForm({
validate,
form: 'LoginForm'
})(Login)
The testing file:
import React from 'react'
import { shallow } from 'enzyme'
import { Login } from './login'
describe('<Login />', () => {
it('render the <Login /> component without crashing', () => {
const wrapper = shallow(<Login />)
expect(wrapper.length).toEqual(1)
})
// it('navigate to /landing page when the form is submit correctly', () => {
// })
})
You are consuming a prop function from your HOC reduxForm on your render method. But on the test file, you are importing the component without the HOC on top of it, which means that prop/function is not available. You have to provide Login with a mock handleSubmit prop function.
Try:
import React from 'react'
import { shallow } from 'enzyme'
import { Login } from './login'
describe('<Login />', () => {
it('render the <Login /> component without crashing', () => {
const wrapper = shallow(<Login handleSubmit={() => {}} />)
expect(wrapper.length).toEqual(1)
})
})
you need pass onSubmit on initialization:
export default reduxForm({
validate,
form: 'LoginForm',
onSubmit: this.onSubmit // here
})(Login)
or in props:
<Component>
<Login onSubmit={this.onSubmit}/>
</Component>
First export the redux-form decorated class
export const DecoratedLogin = reduxForm({
validate,
form: 'LoginForm'
})(Login);
Then use it instead of the plain class.
import { DecoratedLogin } from './login'
and you should be able to access the props from redux-form.
I am using react/redux with redux-form and for some reason the input values are not showing on my edit form.
I console.log my post and it shows that they are there, but for some reason it is not working. I will post code below.
edit component:
import React , { Component } from 'react';
import * as actions from '../../actions/posts_actions';
import { reduxForm, Field } from 'redux-form';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
class EditPost extends Component {
componentDidMount() {
const {id} = this.props.match.params;
this.props.getOnePost(id);
}
renderField(field) {
const { meta: {touched, error} } = field;
const className = `form-group ${touched && error ? 'has-danger' : ''}`;
return (
<div className={className}>
<label><strong>{field.label}:</strong></label>
<input
className="form-control"
type={field.type}
value={field.value}
{...field.input}
/>
<div className="text-help">
{ touched ? error : ''}
</div>
</div>
)
}
onSubmit(values) {
const {id} = this.props.match.params;
this.props.updatePost(values, id, () => {
this.props.history.push(`/posts/${id}`);
});
}
render() {
const {handleSubmit} = this.props;
const {post} = this.props;
if(!post) {
return <div> Loading... </div>;
}
console.log(post);
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Title"
name="title"
type="text"
value={post.title}
component={this.renderField}
/>
<Field
label="Content"
name="content"
type="text"
value={post.content}
component={this.renderField}
/>
<button type="submit" className="btn btn-success">Submit</button>
<Link to={`/posts/${post._id}`} className="btn btn-danger">Cancel</Link>
</form>
);
}
}
function validate(values) {
const errors = {};
if(!values.title) {
errors.title = "Enter a title!";
}
if(!values.content) {
errors.content = "Enter some content please!";
}
return errors;
}
function mapStateToProps({ posts }, ownProps) {
return { post: posts[ownProps.match.params.id] };
}
export default reduxForm({
validate,
form: 'editform'
})(connect(mapStateToProps, actions)(EditPost));
Instead of setting the value directly, pass an initialValues prop:
function mapStateToProps({ posts }, ownProps) {
const post = posts[ownProps.match.params.id]
return {
post,
initialValues: {
...post
}
};
}
remove value props from Fields in your form. So long as the properties on your Post object are the same as the name of the field you want to populate, you can just use object spread. If they are different, you'll have to map them appropriately
initialValues: {
contentField: post.content
}
<Field name="contentField" />
initial values have to be set before you use the reduxForm enhancer. Also, because I know it'll come up (and trust me, it always comes up eventually), if you want your form values to update if your model updates, you'll have add enableReinitialize: true to reduxForm's config
I'm creating a basic portfolio website, that has a Contact form section, where people can send me a message to my email via Formspree. I'm using Redux Forms for this and the form works, but I'd like to reset the form fields and add some kind of 'success' message on successful submit.
I've tried 2 methods: 1) calling this.props.resetForm() and 2) calling dispatch(reset('ContactForm'). Neither have worked.
Here is the code:
contact.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { sendMail } from '../actions';
class Contact extends Component {
renderField(field) {
const { meta: { touched, error } } = field;
const className = `form-group ${touched && error && 'has-error'}`;
return (
<div className={className}>
<label>{field.label}</label>
{field.type == 'text' ?
<input
className='form-control'
type={field.type}
{...field.input}
/> :
<textarea
className='form-control'
rows='5'
{...field.input}
/>}
<div className='help-block'>
{touched && error}
</div>
</div>
);
}
onSubmit(values) {
this.props.sendMail(values, () => {
this.props.resetForm();
});
}
render() {
const { handleSubmit } = this.props;
return (
<div id='contact'>
<h1>Contact</h1>
<p>Feel free to drop me a mail. Whether it's work-related or about coding, tech, entrepreneurship, travel, football or life in general. I'm always looking to connect with other people.</p>
<div id='contact-form'>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label='Name:'
name='name'
type='text'
component={this.renderField}
/>
<Field
label='Email:'
name='email'
type='text'
component={this.renderField}
/>
<Field
label='Message:'
name='message'
type='textarea'
component={this.renderField}
/>
<button
type='submit'
className='btn btn-primary'
>
Submit
</button>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.name) {
errors.name = "Enter a name"
}
if (!values.email) {
errors.email = "Enter an email"
}
if (!values.message) {
errors.message = "Enter a message"
}
return errors;
}
export default reduxForm({
form: 'ContactForm',
validate
})(
connect(null, { sendMail })(Contact)
);
actions/index.js:
import axios from 'axios';
import { reset } from 'redux-form';
export const MAIL_SENT = 'MAIL_SENT';
const ROOT_URL='https://formspree.io/'
const EMAIL = 'xxxxxx#gmail.com'
export function sendMail(values) {
const request = axios.post(`${ROOT_URL}${EMAIL}`, values);
return {
type: MAIL_SENT,
payload: true
};
dispatch(reset('ContactForm'));
}
You need to add a constructor to the class contact.
class Contact extends Component {
constructor(props) {
super(props);
}
...
};
Putting dispatch(reset('ContactForm')) after return syntax, it would never get called. By the way, the action creator is supposed to be a pure function. Adding dispatch action to it is not a good idea.
Hope this help.
Use
this.props.reset()
Using this works on resetting the form after submit.
i try to figure out what's wrong in my code. can someone help me find out whats wrong. i use webstorm for text editing, and it's show "unresolve variable" in
this.props.loginUser(value);
is there any related to this?
this is my code :
import React, { Component } from 'react'
import {reduxForm, Field} from 'redux-form';
import { loginUser } from '../actions/index';
import { Stores } from '../Stores';
import {connect} from 'react-redux';
const validate = values => {
const errors = {};
if (!values.email) {
errors.email = 'Required'
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
if (!values.password) {
errors.password = 'Required'
}
return errors
};
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type} className="form-control"/>
{touched && error && <span className="alert alert-danger">{error}</span>}
</div>
</div>
);
function submit(value){
console.log(value);
this.props.loginUser(value); //didn't work
// Stores.dispatch(loginUser({email,password})); //this method work
}
class LoginV6 extends Component{
render() {
const {handleSubmit, pristine, reset, submitting} = this.props;
return (
<div className="row">
<div className="col-md-6">
<form onSubmit={handleSubmit(submit)}>
<div className="form-group">
<Field
name="email"
type="text"
component={renderField}
label="Email"
/>
</div>
<div className="form-group">
<Field
name="password"
type="password"
component={renderField}
label="Password"
/>
</div>
<div>
<button type="submit" className="btn btn-primary" disabled={pristine||submitting}>
Login
</button>
<button type="button" className="btn btn-primary" disabled={pristine || submitting}
onClick={reset}>
Clear Values
</button>
</div>
</form>
</div>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {
loginUser() {
dispatch({loginUser(value));
}
}
};
function mapStateToProps(state) {
return {
errorMessage: state.auth.error,
authenticated:state.auth.authenticated
}
}
LoginV6 = reduxForm({
form:'LoginV6',
validate
})(LoginV6);
export default LoginV6 = connect(mapStateToProps, mapDispatchToProps)(LoginV6);
this is my action code :
import axios from 'axios';
import jwtdecode from 'jwt-decode';
import {browserHistory} from 'react-router';
import {
AUTH_USER,
AUTH_ERROR,
USER_INFO_SUCCESS,
USER_INFO,
LOGOUT_USER,
GET_TABPANEL,
GET_SETUP_TABTITLES,
} from './types';
const ROOT_URL = 'http://localhost:8000';
// User and Auth actions
//
export function loginUser({email,password}){
return function(dispatch){
axios.post(`${ROOT_URL}/api/auth/login`,{email,password})
.then(response => {
dispatch({type: AUTH_USER,
payload:response.data.token
});
localStorage.setItem('laravel_user_token',response.data.token);
console.log('Login Success');
browserHistory.push("/");
}).catch(()=>{
dispatch(authError("Empty Required Field"));
});
}
}
if i use Stores.dispatch(loginUser(value)); it works.
Change your code like this:
class LoginV6 extends Component{
constructor(){
this.submit = this.submit.bind(this); //Because ES6 is not auto bind
}
submit(value){
//...your code
this.props.loginUser(value); //it will work
// or "loginUser" is an action
this.props.dispatch(loginUser(value)); //see the new function mapDispatchToProps
}
render(){
//...your code
}
}
// ....
// instead map a new object, we map dispatch directly
// with this function, we avoid duplicated/confused about actions in case you have many actions
const mapDispatchToProps = (dispatch) => {
return {
dispatch
}
};
i already fix this by change my code to this :
class LoginV6 extends Component{
submit = (data) => {
console.log(data);
this.props.loginUser(data);
}
render() {
const {handleSubmit, pristine, reset, submitting} = this.props;
return (
<div className="row">
<div className="col-md-6">
<form onSubmit={handleSubmit(this.submit)}>
..............................the rest of code..............................................
const mapDispatchToProps = (dispatch) => {
return {
loginUser: (data) => {
dispatch(loginUser(data))
}
}
}
function mapStateToProps(state) {
return {
errorMessage: state.auth.error,
authenticated:state.auth.authenticated
}
}
LoginV6 = reduxForm({
form:'LoginV6',
validate
})(LoginV6);
export default LoginV6 = connect(mapStateToProps, mapDispatchToProps)(LoginV6);