I'm trying to create a React component that is capable either to create or edit an existing item in my database. Everything is working fine, except one thing: when I call a route new from the edit route (the same component with different behavior), React simply keeps the previous form state and I can't see a new form with empty data. It only works when I call the new route from somewhere else.
Possible routes:
/bastions/new - Renders the component with no data
/bastions/:id - Renders the component with an existing data
Is this problem familiar for someone?
Just some code to make things cleaner (with the most relevant peaces):
Thanks in advance.
class BastionForm extends Component {
componentWillMount() {
if(this.props.bastion.number) {
this.props.fetchBastion(this.props.params.number);
}
}
renderField({ input, label, type, meta: { touched, error, warning } }) {
return (
<FormGroup controlId={label} {...validation}>
<ControlLabel>{label}</ControlLabel>
<FormControl {...input} placeholder={label} type={type} />
<FormControl.Feedback />
</FormGroup>
);
}
render() {
const { handleSubmit, pristine, reset, submitting } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this)) }>
<h3>Create a new Bastion</h3>
<Field name="sample" type="text" component={this.renderField} />
<ButtonToolbar>
<Button bsStyle="primary" disabled={submitting}>Submit</Button>
</ButtonToolbar>
</form>
);
}
}
BastionForm = reduxForm({
form: 'BastionForm',
enableReinitialize: true
})(BastionForm);
BastionForm = connect(
state => ({
initialValues: state.bastions.bastion
}), { fetchBastion }
)(BastionForm);
export default BastionForm;
componentWillMount is only invoked once before mounting occurs. Since /bastions/new and /bastions/:id are actually referring to the same route, BastionForm will not be unmounted when your path changes from /bastions/:id to /bastions/new. So you can decide whether showing an existed data or showing with no data through changing the state of BastionForm(dispatch an action). Then handle the props change in componentWillReceiveProps.
Related
I got component structure like this. The actual code is not like this I am just trying to help you visualize.
<EditAssessment>
<AssessmentForm>
<AssessmentScheduleCronWeekField>
</AssessmentScheduleCronWeekField>
</AssessmentForm>
</EditAssessment>
The thing is when I pass the value from EditAssessment to child component, useformMik is changing the value.
EditAssessment component
// logging the initial value
{console.log(
(study.phases[Number(phaseIndex)] as AssessmentPhase).assessments[
Number(assessmentIndex)
]
)}
{loading ? (
<Loading size="large" label="Saving..." />
) : (
<AssessmentForm
initialValues={
(study.phases[Number(phaseIndex)] as AssessmentPhase).assessments[
Number(assessmentIndex)
] as Assessment
}
onSubmit={onSubmit}
onCancel={onDone}
/>
)}
Here is the AssessmentForm component
import { Field, FormikProvider, useFormik } from 'formik';
export const AssessmentForm = ({
initialValues,
onSubmit,
onCancel,
}: AssessmentFormProps) => {
const [scheduleType, setScheduleType] = useState('empty');
const formik = useFormik({
initialValues,
onSubmit,
validationSchema,
});
// *** THE VALUE CHANGE HERE, THIS IS IMPORTANT ***
console.log(initialValues.schedule);
console.log(formik.values.schedule);
return (
<FormikProvider value={formik}>
<Form data-cy="assessment-form" onSubmitCapture={formik.handleSubmit}>
{/* Assessment Questionnaire ID Field */}
<Field name="questionnaireId" component={AssessmentQuestionIdField} />
<div>Hello</div>
{/* Assessment Schedule Type Form */}
<AssessmentScheduleTypeForm
assessmentSchedule={initialValues.schedule}
onTypeChange={setScheduleType}
/>
<div>Hello 1</div>
{/* Various Assessment Schedule Forms */}
{(() => {
let component = undefined;
switch (scheduleType) {
case 'oneTime':
component = AssessmentScheduleOneTimeField;
break;
case 'cronWeek':
component = AssessmentScheduleCronWeekField;
break;
}
if (component !== undefined) {
return <Field name="schedule" component={component} />;
}
return (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={<p>Select a Schedule Type</p>}
/>
);
})()}
<AssessmentFormScheduleDescription schedule={initialValues.schedule} />
{/* Combined Form using `useFormik() */}
</Form>
</FormikProvider>
);
};
As you can see there are two console.log statement places 1 in EditAssement and inAssessmentForm`
The value passing to the AssessmentForm change automatically and idk why
Here is a screen shot
These console.log when I reach to the path. If you line 53 console.log is coming from useformMik and that one is updating the value
I dont think this is an issue with formik, I tried to make a similar example, but I dont see issues with passing props, original values do arrive in the child component. Of course I dont see all of your code, like what are these new values, where do they come from, maybe you can investigate on that.
As last resort you can remove useFormik and write your own context state, as useFormik is nothing more than advanced context state doc1, doc2.
Hello guys I'm pretty new at React js and I just started using the react-stripe-js. My question is, is it possible to make stay the value in Stepper Elements in React Stepper after changing page? Your answers are very much appreciated.
Sample Design Stepper with Stipe Elements
class CheckoutForm extends React.Component {
handleSubmit = (ev) => {
ev.preventDefault();
this.props.stripe
.createPaymentMethod('card')
.then((payload) => {
console.log('[pm]', payload)
});
this.props.stripe
.createToken({type: 'card', name: 'Jenny Rosen'})
.then((payload) => {
console.log(payload)
});
this.props.stripe.createSource({
type: 'card',
owner: {
name: 'Jenny Rosen',
},
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<CardSection />
<button>Confirm order</button>
</form>
);
}
}
//CardSection.js
class CardSection extends React.Component {
render() {
return (
<>
<label>
Name
<input name="name" type="text" placeholder="Jane Doe" required />
</label>
<label>
Card details
<CardNumberElement style={{base: {fontSize: '18px'}}}/>
<CardExpiryElement style={{base: {fontSize: '18px'}}} />
<CardCVCElement style={{base: {fontSize: '18px'}}} />
</label>
</>
);
}
}
The state is local to your component. Whatever logic you have in next() appears to be selectively rendering depending on which step the user has reached.
This is a problem because when the user moves to the next step, your state values is unmounted and destroyed and therefore loses its state.
The solution is to save values as a prop on your child component, and move the handleChange up into the parent component and have that as a prop on Child Component as well. Store values in the state of the parent component which doesn't unmount on change of step.
In your parent component, put the handleChange event so it stores in the parent state.
Now, as the user moves to the next screen, you have safely stored the selected values in the parent state.
import React from "react";
import { Field, Form } from "react-final-form";
export function LogInDialog(props: { open: boolean; onClose: () => void }) {
const onSubmit = vals => {
alert(JSON.stringify(vals));
};
console.log("logindialog");
return (
<Form
key="unique_key_0"
onSubmit={onSubmit}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit} key="unique_key_1" id="unique_id_1">
<Field
key="unique_key_2"
id="unique_id_2"
name="email"
component={({ input: { onChange, value }, label }) => (
<input
key="unique_key_3"
id="unique_id_3"
type="text"
value={value}
onChange={onChange}
/>
)}
></Field>
</form>
)}
/>
);
}
The input is losing its focus after every keystroke. In devtools, I can see the HTML form is created anew every time (it flashes pink). The React component itself however goes through rendering only once.
There are similar questions, however all of them suggest using unique keys. Such a solution doesn't seem to be working here.
Why is the input losing its focus over and over again? How do I fix it?
https://codesandbox.io/s/busy-torvalds-91zln
Since an inline lambda is used for the component, its identity changes every render.
While according to many other questions an unique key should be enough, moving the component function outside the master component fixes it altogether.
I'm playing with React Hooks - rewriting a form to use hook concepts. Everything works as expected except that once I type any 1 character into the input, the input loses focus.
I guess there is a problem that the outside of the component doesn't know about the internal changes in the component, but how do I resolve this issue?
Here is the useForm Hook:
import React, { useState } from "react";
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
const FormComponent = () => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
return [state, FormComponent, setState];
}
Here is the component that uses the Hook:
function App() {
const [formValue, Form, setFormValue] = useForm("San Francisco, CA", "Location");
return (
<Fragment>
<h1>{formValue}</h1>
<Form />
</Fragment>
);
}
While answer by Kais will solve the symptoms, it will leave the cause unaddressed. It will also fail if there are multiple inputs - which one should autofocus itself on rerender then?
The issue happens when you define a component (FormComponent) inside the scope of another function which is called each render of your App component. This gives you a completely new FormComponent each time your App component is rerendered and calls useState. That new component is then, well, without focus.
Personally I would feel agains returning components from a hook. I would instead define a FormComponent component, and only return state from useForm state.
But, a working example closest to your original code could be:
// useForm.js
import React, { useState } from "react";
// Define the FormComponent outside of your useForm hook
const FormComponent = ({ setState, state, label }) => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
return [
state,
<FormComponent state={state} setState={setState} label={label} />,
setState
];
}
// App.js
import useForm from "./useForm";
export default function App() {
const [formValue, Form] = useForm("San Francisco, CA", "Location");
return (
<>
<h1>{formValue}</h1>
{Form}
</>
);
}
Here's a sandbox
When you enter any text in input box. Parent Component is also re-rendering. So you need to make focus on input manually.
For this, use autoFocus in input tag
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
autoFocus
/>
The above answers didn't work for me. The solution that worked for me was much simpler and, for that reason, less obvious.
The Problem
Essentially, the value that I was changing with the input was also being used for each key in a list of inputs.
Hence, when I updated the value the key would change and React would detect that it's different relative to the last key and create a new input in its place. As a new input it wouldn't focus on itself.
However, by using autoFocus it would automatically focus on the newly created input. But the issue wasn't fixed as it was obvious that the input was continually going through a cycle of un-focus and focus.
Here's an article demonstrating the problem.
The Fix
Update the key to an unchangeable value so React would have a stable reference to the list items. In my case I just updated it to the index. This is not ideal (React docs recommend using a stable ID), but in my situation it was okay because the order of the items won't change.
The first solution actually worked for me , initially the functional component which contained the text field was part of the main functional component , i was facing the same issue but when i extracted the text-field component into another page and imported it it worked fine
This was my component
<Paper >
<div>
<div style={{padding:'10px' ,display:'flex'}} >
<inputstyle={{marginRight:'5px'}} value={val} onChange={(e)=>{setVal(e.target.value)}} />
</div>
</div>
</Paper>
I am using react-kendo-ui. I want to wrap Input from #progress/kendo-react-inputs to use it with ReduxForm. Please find my code below:
import React from 'react'
import { Input } from '#progress/kendo-react-inputs';
const InputText = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<Input {...input} type={type} placeholder={label} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
export default InputText
Call the InputText from another component as below:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Input } from '#progress/kendo-react-inputs';
import InputText from './input-text';
const validateNotEmpty = value => !value ? 'Must enter a value' : null;
const onSubmit = (values) => {
console.log(values);
}
const AddLoc= ({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<div>
<Field
label="Address"
name="address"
component={InputText}
validate={validateNotEmpty}
/>
</div>
<button type="submit">Submit</button>
</form>
)
export default reduxForm({
form: 'AddLoc'
})(AddLoc)
But while typing inside the input text it keeps giving the following error/warning:
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `nativeEvent` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().
While typing inside the input text automatically outputs [object Object]. Please check the image above. Could anyone please let me know what is causing the error.
Thanks
The reduxForm works only with pure DOM <input />. Internally it clones the elements search the children and then attaches the onChange dynamically. So it will not work with the Input and the NumericTextBox from the #progress/kendo-react-inputs package. This statement is based on the official kendo documentation about integrating with the redux-form.
The same author of the redux-form have fork of it called react-final-form which could be used on any component that have Value and onChange props. By our tests it works with the components from #progress/kendo-react-inputs and #progress/kendo-react-dropdowns packages. It looks kendo already have an example using final-form in their integration section.