react - pass function by props in stateless and run in function - reactjs

I want to call a function passed from a parent in react stateless component and run that function in the child (checkBoxhandleChange).
react do not recognize passed function (props) in side child function(checkBoxhandleChange).
I make a function for all of type components (checkbox, input, ....) in the parent and handle all
import React, { Component } from 'react';
import Input from "./base/input";
class Parent extends Component {
state = { data: [] }
handleChange = ({ currentTarget: input }) => {
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data });
console.log("handleChange", input.name, input.value);
};
render() {
const { data } = this.state
return (
<Input
label=" is active ?"
require={true}
type="textarea"
name={"reasons"}
value={data.reasons}
handleChange={this.handleChange}
/>
);
}
}
export default Parent;
child => Input.jsx
import {
Form,
Row,
Col,
} from "react-bootstrap";
import "./style.scss";
const Input = ({
name,
label,
value,
handleChange,
}) => {
function checkBoxhandleChange({ currentTarget: input }) {
console.log([input.name], input.value)
handleChange({ currentTarget: { name: input.name, value: input.checked } })
}
return (
<Row className="my-input">
<Col md={12}>
<Form.Group className="mb-3 col-md-12" controlId="formBasicCheckbox">
<Form.Check type="checkbox" label={label} name={name}
value={value}
onChange={checkBoxhandleChange} />
</Form.Group>
</Col>
</Row>
);
};
export default Input;

First of all I suggest you to remove your checkBoxHandleChange() from Input.jsx and directly call the handleChange() function from your child file using the props.
import { Form, Row, Col } from "react-bootstrap";
import "./style.scss";
const input = (props) => {
<div>
<Row className="my-input">
<Col md={12}>
<Form.Group className="mb-3 col-md-12" controlId="formBasicCheckbox">
<Form.Check type="checkbox" label={label} name={name}
value={value}
onChange={props.changedInput}/>
</Form.Group>
</Col>
</Row>
</div>
};
export default input;
You can then edit your parent file as such by modifying this line handleChange={this.handleChange} to this changedInput={this.handleChange}

Related

Console Log from Props

I'm confused as I have managed to get my data to be logged via different means, but confused as to why when I use props for the data (rather than repeating code) it will not log the input.
For reference, I have a field component that will take props to drive what my react-hook-form TextField will request. I'd like to expand on the component but until it logs my data, I cannot proceed!
Below is the code that actually logs my data:
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { TextField, Button } from "#material-ui/core/";
const NewRequest = () => {
const { register, handleSubmit, control } = useForm();
const onSubmit = (data) => console.log(data);
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name='firstName'
render={({ field: { onChange, onBlur, value, name, ref } }) => (
<TextField
label='First Name'
variant='filled'
size='small'
onBlur={onBlur}
onChange={onChange}
checked={value}
inputRef={ref}
/>
)}
/>
<br />
<br />
<Button type='submit' variant='contained'>
Submit
</Button>
</form>
</div>
);
};
export default NewRequest;
I have then moved the Controller, TextField to create a component:
import React from "react";
import { Controller, useForm } from "react-hook-form";
import { TextField } from "#material-ui/core/";
const TextFieldComponent = (props) => {
const { name, label, size, variant } = props;
const { control } = useForm();
return (
<div>
<Controller
control={control}
name={name}
render={({ field: { onChange, onBlur, value, ref } }) => (
<TextField
label={label}
variant={variant}
size={size}
onBlur={onBlur}
onChange={onChange}
checked={value}
inputRef={ref}
/>
)}
/>
</div>
);
};
export default TextFieldComponent;
Which I am using inside of another component (to generate a full form) and passing through my props (I will make a different component for Button, but for now it is where it is):
import React from "react";
import { useForm, Controller } from "react-hook-form";
import TextFieldComponent from "./form-components/text-field";
import { Button } from "#material-ui/core/";
const NewRequest= () => {
return (
<div>
<TextFieldComponent
name='firstName'
label='First Name'
size='small'
variant='filled'
/>
<br />
<br />
<Button type='submit' variant='contained'>
Submit
</Button>
</div>
);
};
export default NewRequest;
Now pushing that component into an index.js file to render a form:
import React from "react";
import NewVendorForm from "../components/new-vendor-request";
import { useForm } from "react-hook-form";
const Home = () => {
const { handleSubmit } = useForm();
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<NewVendorForm />
</form>
);
};
export default Home;
I'm stumped as to why this way would
a) customise my TextField in my form as intended
b) but not log my data as requested
I'm sure there is a very valid, basic reason as to why and it is my lack of understanding of console logging, but am in need of help to resolve!
Many thanks in advance.
The issue is that, in the refactored code, you're calling useForm twice, each of which generates a different control and data. You probably want to call useForm at the top level only, and pass in whatever you need (in particular control) to the form fields.
const Home = () => {
const { handleSubmit, control } = useForm();
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<NewVendorForm control={control} />
</form>
);
};
const NewRequest= ({control}) => {
return (
<div>
<TextFieldComponent
name='firstName'
label='First Name'
size='small'
variant='filled'
control={control}
/>
<br />
<br />
<Button type='submit' variant='contained'>
Submit
</Button>
</div>
);
};
const TextFieldComponent = (props) => {
const { name, label, size, variant, control } = props;
return (
<div>
<Controller
control={control}
name={name}
render={({ field: { onChange, onBlur, value, ref } }) => (
<TextField
label={label}
variant={variant}
size={size}
onBlur={onBlur}
onChange={onChange}
checked={value}
inputRef={ref}
/>
)}
/>
</div>
);
};

Redux form, loses focus after keypress?

Im experiencing a bit of a weird problem.
I have a form where I have an input field, but after i trigger an onChange event with a keypress it loses focus from the input field...
{editPhoneEnabled && <Field
name="phoneNumber"
component={phoneFieldRenderer}
validate={[Validator.required, Validator.phone]}
/>}
const phoneFieldRenderer = ({ input }) => {
return (
<ReactTelInput {...input} />
)
}
I've seen this example setup where the problem occurs (https://codepen.io/invisiblecomma/pen/wqLaZQ), but i've done the similar as you can see, but it still doesnt work?
Any ideas out there to help me understand what is going on?
The whole component:
import React, { useState, useEffect } from 'react';
import { Field, reduxForm, getFormValues } from 'redux-form';
import { Col, FormGroup } from 'reactstrap';
import { t } from 'Utilities/i18n';
import Validator from 'Utilities/validation';
import Button from 'Components/Forms/Button';
import { connect } from 'react-redux';
import { gql, useMutation } from '#apollo/client';
import ReactTelInput from 'react-telephone-input';
const formName = 'PhoneVerifyForm';
const performPhoneVerificationMutation = gql`
mutation registerPage_userPhoneVerification($input: RegisterPhoneVerificationInput!) {
userPhoneVerification(input: $input) {
errors {
field
messages
}
phoneNumberVerificationSid
verified
}
}
`;
// TODO props: unconfirmedUserId, phoneNumberVerificationSid, initialValues, callback
const PhoneVerifyForm = (props) => {
const [editPhoneEnabled, setEditPhoneEnabled] = useState(false);
const [codeSend, setCodeSend] = useState(props.phoneNumberVerificationSid !== undefined);
const [performPhoneVerification, { data:performPhoneVerificationData }] = useMutation(performPhoneVerificationMutation);
const [errors, setErrors] = useState([]);
useEffect(() => {
if (performPhoneVerificationData && performPhoneVerificationData.userPhoneVerification) {
const {userPhoneVerification} = performPhoneVerificationData;
if(userPhoneVerification.errors.length === 0) {
setErrors([])
if (editPhoneEnabled) {
editPhoneEnabled(false);
}
setCodeSend(userPhoneVerification.phoneNumberVerificationSid !== undefined);
if(userPhoneVerification.verified !== undefined) {
props.callback(props.formValues.phone);
}
} else {
setErrors(userPhoneVerification.errors)
}
}
}, [performPhoneVerificationData, ])
function handleSubmit(values) {
if (editPhoneEnabled) {
// update phone number
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
phone: values.phoneNumber,
},
},
});
} else if (!codeSend) {
// send new code
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
channel: values.channel,
},
},
});
}
// else validate code
return performPhoneVerification({
variables: {
input: {
unconfirmedUserId: props.unconfirmedUserId,
code: values.code,
},
},
});
}
const handleEditPhone = () => {
setEditPhoneEnabled(!editPhoneEnabled);
setCodeSend(false);
};
const phoneFieldRenderer = ({ input }) => {
return (
<ReactTelInput {...input} />
)
}
return (
<form className="row" onSubmit={props.handleSubmit(handleSubmit)}>
{!codeSend && <Col style={{background: 'pink'}}>
<p>{t('select channel')}</p>
<FormGroup row className="indented-form-group">
<Col>
<label>
<Field
name="channel"
component="input"
type="radio"
value="sms"
validate={Validator.required}
/>
{t('Sms')}
</label>
</Col>
<Col xs={6}>
<label>
<Field
name="channel"
component="input"
type="radio"
value="phone"
validate={Validator.required}
/>
{t('phone')}
</label>
</Col>
</FormGroup>
</Col>}
{codeSend && <Col style={{background: 'yellow'}}>
<FormGroup row className="indented-form-group">
<Field
labelClassname="required"
label={t('Code')}
name="code"
component="input"
type="text"
validate={[Validator.required]}
/>
</FormGroup>
</Col>}
<Col style={{background: 'red'}}>
<FormGroup row className="indented-form-group">
{!editPhoneEnabled && <div>
<span>PHONE PLACEHOLDER</span><br />
<span onClick={handleEditPhone}>Edit phone number</span>
</div>}
{editPhoneEnabled && <Field
name="phoneNumber"
component={phoneFieldRenderer}
validate={[Validator.required, Validator.phone]}
/>}
</FormGroup>
</Col>
<Col>
<FormGroup row className="indented-form-group">
<Button submit disabled={props.submitting || props.invalid}>
{editPhoneEnabled ? t('Change phone number') : codeSend ? t('Validate code') : t('Send code')}
</Button>
</FormGroup>
</Col>
</form>
);
};
const mapStateToProps = state => ({
formValues: getFormValues(formName)(state) || {}, //This basically gives us the form values in the props and it gets updated on keydown.
});
const decoratedComponent = connect(mapStateToProps, null)(PhoneVerifyForm)
export default (reduxForm({
form: formName,
enableReinitialize: true,
shouldAsyncValidate: ({ trigger }) => ['blur'].includes(trigger),
})(decoratedComponent));

onChange event doesn't fire on first input

I'm new to React and I've got this registration (parent component) where it has an inventory (child component) and I'm changing the state of the inventory once the user inputs the quantity of the item that they hold so I can register them
But onChange seems to be delayed, I input 4 water bottles for example, and it console logs me the default value, only when I input the amount for another item like food it displays me the 4 water bottles and 0 food :(
This is what I'm working with...
Child component:
import React from "react";
import { Col, Row, FormGroup, Label, Input } from "reactstrap";
import waterIcon from "./../assets/water.png";
import soupIcon from "./../assets/food.png";
import medsIcon from "./../assets/aid.png";
import weaponIcon from "./../assets/gun.png";
function Inventory({ onSubmitInventory, currentInventory }) {
const onChangeWater = (e) => {
const newInventory = { ...currentInventory, water: e.target.value };
onSubmitInventory(newInventory);
};
const onChangeSoup = (e) => {
const newInventory = { ...currentInventory, soup: e.target.value };
onSubmitInventory(newInventory);
};
const onChangeMeds = (e) => {
const newInventory = { ...currentInventory, meds: e.target.value };
onSubmitInventory(newInventory);
};
const onChangeWeapon = (e) => {
const newInventory = { ...currentInventory, weapon: e.target.value };
onSubmitInventory(newInventory);
};
return (
<FormGroup>
<Row className="justify-content-center text-center border-top pt-3">
<Col xs="3">
<img src={waterIcon} alt="Water" />
<Label for="inventoryWater">Fiji Water:</Label>
<Input
type="number"
name="inventoryWater"
id="inventoryWater"
placeholder="Enter amount..."
onChange={onChangeWater}
/>
</Col>
<Col xs="3">
<img src={soupIcon} alt="Soup" />
<Label for="inventorySoup">Campbell Soup:</Label>
<Input
type="number"
name="inventorySoup"
id="inventorySoup"
placeholder="Enter amount..."
onChange={onChangeSoup}
/>
</Col>
<Col xs="3">
<img src={medsIcon} alt="Aid" />
<Label for="inventoryMeds">First Aid Pouch:</Label>
<Input
type="number"
name="inventoryMeds"
id="inventoryMeds"
placeholder="Enter amount..."
onChange={onChangeMeds}
/>
</Col>
<Col xs="3">
<img
className="d-block"
style={{ margin: "0 auto" }}
src={weaponIcon}
alt="Gun"
/>
<Label for="inventoryWeapon">AK47:</Label>
<Input
type="number"
name="inventoryWeapon"
id="inventoryWeapon"
placeholder="Enter amount..."
onChange={onChangeWeapon}
/>
</Col>
</Row>
</FormGroup>
);
}
export default Inventory;
Parent component:
import React, { useState } from "react";
import { Col, Row, Button, Form, Label, Input } from "reactstrap";
import { useForm } from "react-hook-form";
import Inventory from "./Inventory";
import MapContainer from "./MapContainer";
function Register() {
const [lonlat, setLonlat] = useState("");
const onMarkerChange = (lonlat) => {
setLonlat(lonlat);
};
const [inventory, setInventory] = useState({
water: 0,
soup: 0,
meds: 0,
weapon: 0,
});
const onSubmitInventory = (newInventory) => {
setInventory(newInventory);
console.log(inventory);
};
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data) => {
console.log(inventory);
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: data.personName }),
};
console.log(data);
};
return (
<Form
id="registerForm"
name="registerForm"
onSubmit={handleSubmit(onSubmit)}
>
<h4>New Person</h4>
<Row className="border-top pt-4">
<Col xs="8">
<Label for="personName">Your name:</Label>
<Input
className="gray-input"
type="text"
name="name"
id="personName"
placeholder="Enter your name here..."
innerRef={register}
/>
</Col>
<Col xs="2" className="text-center">
<Label for="personAge">Age:</Label>
<Input
className="gray-input"
type="number"
name="age"
id="personAge"
placeholder="Enter age..."
innerRef={register}
/>
</Col>
<Col xs="2" className="text-center">
<Label for="personGender">Gender:</Label>
<Input
type="select"
name="gender"
id="personGender"
innerRef={register}
>
<option defaultValue disabled>
-
</option>
<option>F</option>
<option>M</option>
</Input>
</Col>
</Row>
<Row>
<Col xs="12">
<Input
hidden
id="personLatLon"
name="personLatLon"
type="text"
defaultValue={lonlat}
innerRef={register}
/>
<MapContainer onMarkerChange={onMarkerChange} />
</Col>
</Row>
<h4>Inventory</h4>
<Inventory
onSubmitInventory={onSubmitInventory}
currentInventory={inventory}
/>
<Button
outline
color="secondary"
className="mt-2"
type="submit"
form="registerForm"
>
Submit
</Button>
</Form>
);
}
export default Register;
EDIT: Updated code with tips from answers, still facing the same problem :(
The only problem I see with your code is that you are trying to pass a second argument to setInventory. I don't believe this works with hooks like it did with class components. When I attempt to type that in typescript it throws an instant error.
Just pass the actual data you are trying to send and then call onSubmitInventory(inventory) for example:
const onChangeWeapon = (e) => {
const newInventory = {...inventory, weapon: e.target.value};
setInventory(newInventory);
onSubmitInventory(newInventory);
};
If you have to wait for the next render to call onSubmitInventory, it should be a useEffect in the parent. My next question would be why the parent needs the state and the child also has it as a state? Possibly it should be lifted up. But I'm not sure of that without seeing the parent.
Edit: Is your problem just with the console.log? If you run
const [state, setState] = useState(true);
// ...
setState(false);
console.log(state); // outputs true.
The value of state does not change until the next render. Calling setState will cause a new render, but until that happens, the old value is there. Add a console.log of state in the parent just in the body like here
const [inventory, setInventory] = useState({
water: 0,
soup: 0,
meds: 0,
weapon: 0,
});
console.log('I just rendered, inventory: ', inventory);
You'll see it updates!
The fact that the value of state does not change can bite you in this case:
const [state, setState] = useState(true); // closes over this state!
// ...
setState(!state); // closes over the state value above
setState(!state); // closes over the same state value above
// state will be false on next render
So I use this form if there is any possibility I call setState twice before a render:
const [state, setState] = useState(true);
// ...
setState(state => !state);
setState(state => !state);
// state will be true on next render
Inside your parent component's onSubmitInventory you have named the argument inventory but that already exists in the parent's scope. I suggest renaming that newInventory to be clear about which you are referencing.
Also, it seems like you are keeping track of inventory in both the parent and child's state. Keep it in the parent and pass it down to the child as a prop/props.

axios post method just not responding with anything

i have this axios post method in my Action creator in my reactJS app but for some reason, I don't understand why, the post method is frozen and doesn't do anything. I have a similar implementation in my one of the other project that's working fine but this isn't. The console.log() shows nothing, no error no response. Please help, thanks in advance.
export const addNewPost = (data) =>(dispatch) => {
dispatch(newPostLoading);
console.log("addNewPost triggered");
return (dispatch) => {
axios
.post("/posts.json", data)
.then((response) => {
console.log(response.data);
dispatch(newPostSuc());
})
.catch((err) => {
console.log(err);
dispatch(newPostFailed(err));
});
};
};
This is what my caller component looks like :
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import { Button, Row, Col, Label } from 'reactstrap';
import { Control, LocalForm } from 'react-redux-form';
import { addNewPost } from '../redux/ActionCreators';
import {Loading} from './LoadingComponent';
class NewPost extends Component {
addPostHandler = (event) => {
const data ={
title: event.title,
author: event.author,
content: event.content,
image:''
}
this.props.addNewPost(data);
};
render() {
if(this.props.newPost.isLoading)
{
return (<Loading/>);
}
let redirectElement = null;
if (this.props.newPost.redirect) {
redirectElement = <Redirect to="/" />;
}
return (
<>
{redirectElement}
<div className="container">
<LocalForm onSubmit={(values) => this.addPostHandler(values) }>
<Row className="form-group">
<Label htmlFor="title" md={2}><strong>Post Title</strong></Label>
<Col md={10}>
<Control.text model=".title" id="title" name="title"
placeholder="Catchy title here bitch"
className="form-control"
/>
</Col>
</Row>
<Row className="form-group">
<Label htmlFor="author" md={2}><strong>Author</strong></Label>
<Col md={10}>
<Control.text model=".author" id="author" name="author"
placeholder="Author"
className="form-control"
/>
</Col>
</Row>
<Row className="form-group">
<Label htmlFor="content" md={2}><strong>Post Description</strong></Label>
<Col md={10}>
<Control.textarea model=".content" id="content" name="content"
rows="12" placeholder="You saw shit? Time to step on it"
className="form-control" />
</Col>
</Row>
<Row className="form-group">
<Col md={{size:10, offset: 2}}>
<Button type="submit" color="primary">
Send Post
</Button>
</Col>
</Row>
</LocalForm>
</div>
</>
);
}
}
const mapStateToProps = (state) => {
return {
newPost: state.newPost
};
};
const mapDispatchToProps = (dispatch) => {
return {
addNewPost: (data) => {dispatch(addNewPost(data))}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(NewPost);

State is not passing correctly with redux

Hey i have two form that i want to keep there state seperatly, i added redux and added reducer to keep the state change, but my problem is when i change on form state it's add to the other form state his state also, i want to keep each state seperately
like you see in picture the replace property have been added to the newProjectForm when it only need to be at the roof form.
I have the same reducer on forms because i only need to keep track only onInputChange.
This are my two form:
First Form:
import React from 'react'
import {Form, FormGroup, ControlLabel, FormControl, HelpBlock, Button, ButtonToolbar,
Toggle } from 'rsuite';
import InputPicker from '../input-picker';
import './form.css'
import {onInputChanged} from '../../redux/actions/form.actions';
import { connect } from 'react-redux';
//onChange = {props.onInputChanged} formValue = {props.newProjectForm}
const options = ["yes","no"];
function NewProjectForm(props){
return (
<Form className='form' layout="horizontal" >
<h1 className='title'>New Project</h1>
<br/>
<FormGroup>
<ControlLabel>Address</ControlLabel>
<FormControl name="address" />
<HelpBlock tooltip>Required</HelpBlock>
</FormGroup>
<FormGroup>
<ControlLabel>Affilate</ControlLabel>
<InputPicker name="affilate" data ={options} onChange={props.onInputChanged} style={{width:'300px'}}/>
<HelpBlock tooltip>Required</HelpBlock>
</FormGroup>
<FormGroup>
<ControlLabel>Size</ControlLabel>
<FormControl name="size" type="number" />
</FormGroup>
<FormGroup>
<ControlLabel>Bedroom Amount</ControlLabel>
<FormControl name="bedrooms" type="number" />
</FormGroup>
<FormGroup>
<ControlLabel>Bathrooms amount</ControlLabel>
<FormControl name="bathrooms" type="number" />
</FormGroup>
<FormGroup>
<ControlLabel>Stories</ControlLabel>
<FormControl name="stories" type="number" />
</FormGroup>
<FormGroup>
<ControlLabel>Has Gas</ControlLabel>
<Toggle name="gas"/>
</FormGroup>
<FormGroup>
<ButtonToolbar>
<Button appearance="primary">Save</Button>
<Button appearance="default">Discard</Button>
</ButtonToolbar>
</FormGroup>
</Form>
);
}
const mapStateToProps = (state) => {
return {
newProjectForm: state.newProjectForm
}
};
const mapDispatchToProps = (dispatch) => {
return {
onInputChanged: (event) => dispatch(onInputChanged(event))
}
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(NewProjectForm);
Second Form:
import React from 'react'
import {Form, FormGroup, ControlLabel, FormControl, HelpBlock, Button, ButtonToolbar} from 'rsuite';
import './form.css'
import {onInputChanged} from '../../redux/actions/form.actions';
import { connect } from 'react-redux';
import InputPicker from '../input-picker';
function RoofForm(props){
//replace all-roof type description color
const options = ["yes","no"];
// console.log(props);
//onChange={props.onInputChanged}
return (
<Form className='form' layout="horizontal" onChange = {props.onInputChanged} formValue = {props.roofForm}>
<h1 className='title'>Roof</h1>
<br/>
<FormGroup>
<ControlLabel>Replace ?</ControlLabel>
<InputPicker name="replace" style={{ width: 300 }} data={options} onChange={props.onInputChanged}/>
<HelpBlock tooltip>Required</HelpBlock>
</FormGroup>
<FormGroup>
<ControlLabel>All Roof?</ControlLabel>
<InputPicker name="all-roof" style={{ width: 300 }} data={options} onChange={props.onInputChanged}/>
<HelpBlock tooltip>Required</HelpBlock>
</FormGroup>
{props.roofForm['all-roof'] === 'yes' && (<div><FormGroup>
<ControlLabel>Type</ControlLabel>
<InputPicker name="type" style={{ width: 300 }} />
</FormGroup>
<FormGroup>
<ControlLabel>Description</ControlLabel>
<InputPicker name="description" style={{ width: 300 }} />
</FormGroup>
{props.roofForm.type === 'shingles' && <FormGroup>
<ControlLabel>Color</ControlLabel>
<InputPicker name="color" style={{ width: 300 }} />
</FormGroup>}
</div>)
}
<FormGroup>
<ControlLabel>Rain diverter</ControlLabel>
<FormControl name="rain-diverter" type="number" style={{ width: 300 }} />
</FormGroup>
<FormGroup>
<ControlLabel>Drip edge</ControlLabel>
<FormControl name="drip-edge" type="number" style={{ width: 300 }}/>
</FormGroup>
<FormGroup>
<ButtonToolbar>
<Button appearance="primary">Save</Button>
<Button appearance="default">Discard</Button>
</ButtonToolbar>
</FormGroup>
</Form>
);
}
const mapStateToProps = (state) => {
return {
roofForm: state.roofForm
}
};
const mapDispatchToProps = (dispatch) => {
return {
onInputChanged: (event) => dispatch(onInputChanged(event))
}
};
export default connect(
mapStateToProps,
mapDispatchToProps)
(RoofForm);
and this is my redux setup:
Form Actions:
export const ON_INPUT_CHANGED = 'ON_INPUT_CHANGED';
export function onInputChanged(event) {
console.log(event);
return(
{
type: ON_INPUT_CHANGED,
payload: event
}
)
}
and this is my reducer:
import {ON_INPUT_CHANGED} from '../actions/form.actions';
function formReducerWrapper(defaultState){
return (state=defaultState, action) => {
switch(action.type){
case ON_INPUT_CHANGED:
state = {
...state,
...action.payload
}
break;
default:
return state;
}
return state;
}
}
const newProjectDefault = {
affilate: {label:"", value: ""}
}
const roofDefault ={
replace:{label:"",value:""},
"all-roof":{label:"",value:""},
type:{label:"",value:""},
description: {label:"",value:""},
color:{label:"",value:""}
}
export const newProjectReducer = formReducerWrapper(newProjectDefault);
export const roofReducer = formReducerWrapper(roofDefault);
Thanks in Advance:)
It looks like you are trying to merge the input change event directly into redux state. That is not how it works.
You can access the HTML input element that changed from the change event using event.target and then read the latest value event.target.value.
If you want the form data to stay separate, then you need to dispatch different information for each form. For example:
dispatch({ type: 'input-changed', form: 'newProject', field: 'affiliate', value: event.target.value });
In your reducers, it should skip any events for a different form.
if (event.form !== 'newProject') return state;
If it becomes tedious there are helper packages like redux-form to help structure the application to avoid repetitive code. It also works fine to store form data in local state instead of putting it into redux.

Resources