I am trying to create a React Native app that can create and edit entities.
For example, users.
I have a formik form that takes first and last name, as well as an email address.
If a user params is passed, then the form should take on those values.
However, it only works the first time. After that, the form keeps the first values.
How can I force the form to be reevaluated?
function FormScreen({ navigation, route }) {
const formikRef = React.createRef();
const initialValues = { firstName: "", lastName: "", email: "" };
if (route.params && route.params.user) {
if (route.params.user.firstName) {
initialValues.firstName = route.params.user.firstName;
}
if (route.params.user.lastName) {
initialValues.lastName = route.params.user.lastName;
}
if (route.params.user.email) {
initialValues.email = route.params.user.email;
}
}
const unsubscribeBlur = navigation.addListener("blur", (e) => {
console.log("form blur");
if (formikRef.current) {
console.log("form reset");
formikRef.current?.resetForm();
}
});
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Formik
innerRef={formikRef}
initialValues={initialValues}
validationSchema={Yup.object({
firstName: Yup.string()
.max(15, "Must be 15 characters or less")
.required("Required"),
lastName: Yup.string()
.max(20, "Must be 20 characters or less")
.required("Required"),
email: Yup.string()
.email("Invalid email address")
.required("Required"),
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
<Form>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" type="text" />
<ErrorMessage name="firstName" />
<label htmlFor="lastName">Last Name</label>
<Field name="lastName" type="text" />
<ErrorMessage name="lastName" />
<label htmlFor="email">Email Address</label>
<Field name="email" type="email" />
<ErrorMessage name="email" />
<button type="submit">Submit</button>
</Form>
</Formik>
</View>
);
}
export default FormScreen;
I have code in there to reset the form on blur but the reset doesn't seem to do anything.
You can find the complete code in a snack.
https://snack.expo.io/#hackzilla/create-and-edit-with-formik
Because you are using a tab navigator. The home and form screen will not reMount when you toggle between them.So the formik will not load the new initialValues.In your way,you can reset the form using route params everytime the form screen is focused.But a better way is using a stack navigator,every time create a form using a new instance.
useFocusEffect(
React.useCallback(() => {
if (formikRef.current) {
console.log("form reset");
formikRef.current?.setValues(initialValues);
}
})
);
We can reset the form after submit, so that it will clear all the fields. Please try by replacing your submit method with below mentioned code.
onSubmit={(values, { setSubmitting, resetForm }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
// reset the fields after submit
resetForm();
}, 400);
}}
Related
I am trying to upload an image along with a title and some text for a post. I am using Formik form with an onSubmit in the client folder of the project :
const [charCount, setCharCount] = useState(0);
const [fileObject, setFileObject] = useState(null);
const initialValues = {
title: "",
postText: "",
file: null
};
const validationSchema = Yup.object({
title: Yup.string().required("Required"),
postText: Yup.string().required("Required"),
});
const onSubmitHandler = (values) => {
const formData = new FormData();
formData.append("title", values.title);
formData.append("postText", values.postText);
console.log(fileObject);
formData.append("file", document.getElementById("file").files[0]);
console.log(formData);
axios
.post(
"http://localhost:3001/posts/create",
formData,
{
headers: {
accessToken: localStorage.getItem("token"),
},
}
)
.then((response) => {
console.log(response.data);
});
};
return (
<>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmitHandler}
>
<Form className="create-post-form">
<input type="file" name="file" id="file" onChange={(e) => {
setFileObject(e.target.files[0]);
}}/>
<Field id="input-create-post" name="title" placeholder="Post Title" />
<ErrorMessage name="title" component="span" className="error" />
<Field
id="input-create-post-text"
name="postText"
placeholder="What's the story?"
as="textarea"
onKeyUp={(e) => setCharCount(e.target.value.length)}
maxLength="250"
/>
<div className="char-count">
<span id={charCount===250 ? "char-count-max" : "char-count"}>{charCount}/250</span>
</div>
<ErrorMessage name="title" component="span" className="error" />
<Button type="submit" title="Post" onSubmit={onSubmitHandler} />
</Form>
</Formik>
</>
);
};
On the server side (server folder), in the Posts.js route where I am handling the request :
router.post("/create",validateToken, async(req,res)=>{
if(req.files === null){
return res.status(400).json({message: "No file uploaded"});
}
const file = req.files;
console.log(file);
res.json(req.body)
})
On submitting the form, an empty object {} is logged on the console. And the res.json(req.body) returns the title and postText successfully, but the file is {} (empty). How do I fix this? Please help!
I tried submitting the form with the above code and got this response:
result image
I need the file object to be the actual fileObject that I have as a state.
thanks a lot for taking a look at my problem.
I've been building a button to handle a Mailchimp API call but I can't seem to be able to piece it all together using useState. I would really appreciate some help with this as it is currently throwing 500 errors when successfully added to the mailing list with new email addresses and the response from Mailchimp seems to be fine.
I know there is probably some sort of react hook or other shortcut I could use but I really want to make this work..! hahaa!
api/subscribe.js
export default function handler(req, res) {
const { email } = req.body;
console.log(`email: ${email}`);
if (!email) {
return res.status(400).json({ error: 'Email is required' });
}
try {
const client = require('#mailchimp/mailchimp_marketing');
const API_KEY = process.env.MAILCHIMP_API_KEY;
const API_SERVER = process.env.MAILCHIMP_API_SERVER;
const LIST_ID = process.env.MAILCHIMP_AUDIENCE_ID;
client.setConfig({
apiKey: API_KEY,
server: API_SERVER,
});
const run = async () => {
const response = await client.lists.addListMember(LIST_ID, {
email_address: `${email}`,
status: 'pending',
skip_merge_validation: true,
});
console.log(response);
};
run();
if (response.status >= 400) {
return res.status(400).json({
error: `There was an error subscribing your email address to the newsletter.
Please contact us directly so we can add you in manually. Sorry for any inconvenience.`,
});
}
return res.status(201).json({
message: 'Success! 🎉 You are now subscribed to the newsletter.',
});
} catch (err) {
console.log(res.error);
return res.status(500).json({ error: '***ERROR***' });
}
}
My messy but functional code..!
components/Contact.js
import React, { useState } from 'react';
import LoadingSpinner from '../components/LoadingSpinner';
import { CountdownCircleTimer } from 'react-countdown-circle-timer';
export default function Contact() {
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
// const [loading, setLoading] = useState(true);
const [state, setState] = useState('IDLE');
const subscribe = async (e) => {
e.preventDefault();
setState('LOADING');
setMessage(null);
try {
const res = await fetch('/api/subscribe', {
body: JSON.stringify({
email: email,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
});
console.log(res);
if (res.status >= 400) {
console.log(res.error);
setState('ERROR');
setMessage(
<div>
An error has occurred, please{' '}
<a
href="/contact"
target="_blank"
rel="noreferrer"
className="text-blue-500 underline cursor-pointer transition-transform duration-7000 hover:text-gray-200"
onClick={() => {
setState('IDLE');
setMessage(null);
setEmail('');
}}
>
contact us
</a>
</div>
);
setEmail('');
setTimeout(() => {
setState('IDLE');
setMessage(null);
}, 10000);
return;
}
setState('SUCCESS');
setMessage('Success! 🎉 You are now subscribed to the newsletter.');
setEmail('');
setTimeout(() => {
setState('IDLE');
setMessage(null);
}, 10000);
return;
} catch (e) {
setState('ERROR');
console.log(e.res.error);
setMessage(e.res.error);
setEmail('');
setTimeout(() => {
setState('IDLE');
setMessage(null);
}, 10000);
return;
}
};
return (
<div className="h-[15rem] xs:h-[13.5rem] xsm:h-[12.5rem] sm:h-[13rem] w-full bg-black/90 px-2">
<div className="flex justify-center items-center">
<h1 className="text-center text-zinc-300 text-lg md:text-2xl mt-4">
Sign up to the Newsletter...
</h1>
</div>
<div className="flex justify-center items-center w-full">
<form onSubmit={subscribe}>
<input
type="email"
id="email-input"
name="email"
placeholder="Your#email.here"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
autoCapitalize="off"
autoCorrect="off"
aria-autocomplete="list"
className="h-[40px] min-w-[270px] md:min-w-[400px] border border-black/60 rounded-xl w-full text-center text-md px-2"
/>
<div className="flex justify-center items-center py-2">
{state === 'IDLE' && (
<button
type="submit"
value=""
name="subscribe"
disabled={state === 'LOADING'}
className="w-[200px] h-[40px] px-12 mt-2 text-lg border border-black/60 rounded-xl bg-gray-200 text-gray-900 transition-colors duration-700 transform hover:bg-gray-500 hover:text-gray-100"
>
Subscribe
</button>
)}
{state === 'ERROR' && (
<div className="h-[50px]">
<CountdownCircleTimer
isPlaying
size={55}
strokeWidth={2}
duration={10}
trailColor="#3399ff"
rotation="clockwise"
colors="#ffffff"
>
{({ remainingTime }) => remainingTime}
</CountdownCircleTimer>
</div>
)}
{state === 'SUCCESS' && (
<div className="h-[50px]">
<CountdownCircleTimer
isPlaying
size={50}
duration={10}
colors={[
['#004777', 0.33],
['#F7B801', 0.33],
['#A30000', 0.33],
]}
>
{({ remainingTime }) => remainingTime}
</CountdownCircleTimer>
</div>
)}
{state === 'LOADING' && (
<button
type="submit"
value=""
disabled
name="subscribe"
className="w-[200px] h-[40px] px-12 mt-2 text-lg border border-black/60 rounded-xl bg-gray-200 text-gray-900 transition-colors duration-700 transform"
>
<div className="flex justify-center items-center">
<LoadingSpinner />
</div>
</button>
)}
</div>
<div className="text-zinc-300 text-sm md:text-lg text-center md:min-w-[35ch]">
{message
? message
: `We only send emails when we have genuine news.`}
</div>
</form>
</div>
</div>
);
}
response from Mailchimp (with sensitive info taken out)
email: becir28216#jernang.com
undefined
{
id: '',
email_address: 'becir28216#jernang.com',
unique_email_id: '',
contact_id: '',
full_name: '',
web_id: ,
email_type: 'html',
status: 'pending',
consents_to_one_to_one_messaging: true,
merge_fields: { FNAME: '', LNAME: '' },
stats: { avg_open_rate: 0, avg_click_rate: 0 },
ip_signup: '',
timestamp_signup: '2022-11-15T20:46:33+00:00',
ip_opt: '',
timestamp_opt: '',
member_rating: 2,
last_changed: '2022-11-15T20:46:33+00:00',
language: '',
vip: false,
email_client: '',
location: {
latitude: 0,
longitude: 0,
gmtoff: 0,
dstoff: 0,
country_code: '',
timezone: '',
region: ''
},
source: 'API - Generic',
tags_count: 0,
tags: [],
list_id: '',
...}
I want to change the state of the button according to the response from the API call. I have been trying all sorts of combinations and now my head hurts..
Expected behaviour:
When it is idle, button says Subscribe and the normal message is displayed below it.
when it is loading, the button displays the loading spinner and still displays the normal message
When there is an error, the button displays the countdowntimer and the error message.
When the API call is a success, the loading spinner disappears, the success message is displayed and then 10 seconds later, the whole form resets again.
I have form fn
code here
#update code. I initialization
query = {
field: '',
email: '',
}
not initialization url
export function HomePage() {
const [query, setQuery] = useState({
field: '',
email: '',
});
const handleChaneValue = (value) => {
// not received data on change
console.log('query', query);
setQuery({
...query,
[value.sender.name]: value.newValue
})
}
console.log('query2', query);
query2 received data on change
console.log('query2', query);
#Update code: I add
return (
<div>
<Container
padding={10}
platformConfig={{
desktop: {
maxWidth: 400,
}
}}
>
<FormPanel
padding={10}
shadow
defaults={{
errorTarget: 'under'
}}
margin="0 0 20 0"
title="Using Validators"
>
<Textfield
required
label="Required Field"
requiredMessage="This field is required."
errorTarget="under"
name="field"
onChange={handleChaneValue}
/>
<EmailField
label="Email"
validators="email"
errorTarget="under"
name="email"
onChange={(e) => handleChaneValue(e)}
/>
<Urlfield
label="URL"
validators={{
type: 'url',
message: 'Website url example http://'
}}
errorTarget="under"
name="url"
onChange={handleChaneValue}
/>
<Container layout="center">
<Button
ui="confirm"
text="BTN submit"
handler={handleClick}
style={{border: '1px solid black' }}/>
</Container>
</FormPanel>
</Container>
</div>
)
}
export default HomePage;
When I change value in TextField. Query is
query2: {field: 'abc'}
But i change value in Email field. Query is not give old value "{filed: 'abc'}" throught I use ES6 three dot.
query2 : {email: 'xyz'}
and Query in funciton always initialization
query: {}
image change value
#Update image: when I change value Url. fn handleChangeValue get initialization query
query: {
field: '',
email: '',
}
does not value query updated.
here issues were you did not initialize the value of textfield and emailfield I updated code pls check now
export function HomePage() {
const [query, setQuery] = useState({
field:'',
email:''
});
const handleChaneValue = (value) => {
// not received data on change
console.log("query", query);
setQuery({
...query,
[value.sender.name]: value.newValue,
});
};
console.log("query2", query);
return (
<div>
<Container
padding={10}
platformConfig={{
desktop: {
maxWidth: 400,
},
}}
>
<FormPanel
padding={10}
shadow
defaults={{
errorTarget: "under",
}}
margin="0 0 20 0"
title="Using Validators"
>
<Textfield
required
label="Required Field"
requiredMessage="This field is required."
errorTarget="under"
name="field"
onChange={handleChaneValue}
value={query.field}
/>
<EmailField
label="Email"
validators="email"
errorTarget="under"
name="email"
value={query.email}
onChange={handleChaneValue}
/>
<Container layout="center">
<Button
ui="confirm"
text="BTN submit"
handler={handleClick}
style={{ border: "1px solid black" }}
/>
</Container>
</FormPanel>
</Container>
</div>
);
}
export default HomePage;
I found the solution to the problem. I using framework ExtReact so when run function, states re-initialized initil value. To solve this problem you can useRef and refer here.
enter link description here
Hi I am new to react native.
I have react native project in which I have implement multistep form wizard with formik yup validation. Currently I have implemented multistep form wizard with formik yup validation in react.
How can I implement or convert it to react native.
Below is the code which implement in react.
import React, { useState } from 'react';
import { ErrorMessage, Field, Form, Formik } from "formik";
import * as Yup from "yup";
const Wizard = ({ children, initialValues, onSubmit }) => {
const [stepNumber, setStepNumber] = useState(0);
const steps = React.Children.toArray(children);
const [snapshot, setSnapshot] = useState(initialValues);
const step = steps[stepNumber];
const totalSteps = steps.length;
const isLastStep = stepNumber === totalSteps - 1;
const next = values => {
setSnapshot(values);
setStepNumber(Math.min(stepNumber + 1, totalSteps - 1));
};
const previous = values => {
setSnapshot(values);
setStepNumber(Math.max(stepNumber - 1, 0));
};
const handleSubmit = async (values, formikBag) => {
if (isLastStep) {
return onSubmit(values, formikBag);
} else {
next(values);
}
};
return (
<Formik
initialValues={snapshot}
onSubmit={handleSubmit}
validationSchema={step.props.validationSchema}
>
{formik => (
<Form>
<p>
Step {stepNumber + 1} of {totalSteps}
</p>
{step}
<div style={{ display: "flex" }}>
{stepNumber > 0 && (
<button onClick={() => previous(formik.values)} type="button">
Back
</button>
)}
<div>
<button disabled={formik.isSubmitting} type="submit">
{isLastStep ? "Submit" : "Next"}
</button>
</div>
</div>
</Form>
)}
</Formik>
);
};
const WizardStep = ({ children }) => children;
const Demo = () => {
const submitform=(values)=>{
alert('First name:'+values.firstName+' Last name:'+values.lastName+' Mobile:'+values.mobile+' Email:'+values.email);
}
return(
<>
<Wizard
initialValues={{
email: "",
firstName: "",
lastName: "",
mobile:''
}}
onSubmit={values => submitform(values)}
>
<WizardStep
validationSchema={Yup.object({
firstName: Yup.string().required("required"),
lastName: Yup.string().required("required")
})}
>
<div>
<label htmlFor="firstName">First Name</label>
<Field
autoComplete="given-name"
component="input"
id="firstName"
name="firstName"
placeholder="First Name"
type="text"
/>
<ErrorMessage className="error" component="div" name="firstName" />
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<Field
autoComplete="family-name"
component="input"
id="lastName"
name="lastName"
placeholder="Last Name"
type="text"
/>
<ErrorMessage className="error" component="div" name="lastName" />
</div>
</WizardStep>
<WizardStep
validationSchema={Yup.object({
mobile: Yup.number('Invalid mobile').min(10,"Invalid mobile").required("required")
})}
>
<div>
<label htmlFor="mobile">Mobile</label>
<Field
autoComplete="mobile"
component="input"
id="mobile"
name="mobile"
placeholder="Mobile"
type="text"
/>
<ErrorMessage className="error" component="div" name="mobile" />
</div>
</WizardStep>
<WizardStep
validationSchema={Yup.object({
email: Yup.string()
.email("Invalid email address")
.required("required")
})}
>
<div>
<label htmlFor="email">Email</label>
<Field
autoComplete="email"
component="input"
id="email"
name="email"
placeholder="Email"
type="text"
/>
<ErrorMessage className="error" component="div" name="email" />
</div>
</WizardStep>
</Wizard>
</>
)
}
function App() {
return (
<div className="App">
<Demo />
</div>
);
}
export default App;
I have a material UI table in a react project, and I want a user to click on the pencil/Edit Icon to edit the table row.
This is a loaded question, but would this require an additional piece of Material UI? what would the logic look like to make this happen?? I have most of the code set up on the backend, but don't know how to write this code in the component???
Thanks for looking!
//STYLE VARIABLE BOR MATERIAL BUTTON
const style = {
margin: 12
//
};
const mapStateToProps = (state) => ({
user: state.user,
reduxState: state.getExpense
});
class ExpenseTable extends Component {
constructor(props) {
super(props);
this.state = {
getExpense: []
};
}
isSelected = (index) => {
return this.state.selected.indexOf(index) !== -1;
};
handleRowSelection = (selectedRows) => {
this.setState({
selected: selectedRows,
});
};
//on page load, DISPATCH GET_EXPENSE is
//SENT TO expenseSaga which then
//goes to getExpenseReducer and appends EXPENSE_DATA to the
//DOM
componentDidMount() {
const { id } = this.props.match.params;
this.props.dispatch({ type: USER_ACTIONS.FETCH_USER });
this.props.dispatch({ type: 'GET_EXPENSE' });
}
componentDidUpdate() {
if (!this.props.user.isLoading && this.props.user.userName ===
null) {
this.props.history.push('home');
}
}
logout = () => {
this.props.dispatch(triggerLogout());
// this.props.history.push('home');
};
//SETS STATE FOR ALL INPUTS
handleChange = (name) => {
return (event) => {
this.setState({
[name]: event.target.value
});
};
};
//SUBMIT BUTTON- TRIGGERS DISPATCH TO EXPENSE SAGA TO ADD DATA
handleClick = () => {
console.log('add expense', this.state);
this.props.dispatch({
type: 'ADD_EXPENSE',
payload: this.state
});
};
//TRASH ICON-TRIGGERS DISPATCH TO EXPENSE SAGA DELETE
handleClickRemove = (id) => {
console.log('delete expense', this.state);
this.props.dispatch({
type: 'DELETE_EXPENSE',
payload: id
});
};
handleClickEdit = (row) => {
console.log('edit expense', this.state)
this.props.dispatch({
type: 'EDIT_EXPENSE',
payload: row
})
}
render() {
console.log('HEY-oooo expense render', this.state);
let content = null;
if (this.props.user.userName) {
//MAP OVER REDUX STATE.
const tableRows = this.props.reduxState.map((row) => {
//.MAP SEPARATES DATA INTO INDIVIDUAL ITEMS.
const { id, item_description, purchase_date,
item_price, item_link } = row;
return (
<TableRow selectable={false}>
{/* TABLE ROWS */}
<TableRowColumn>{item_description}
</TableRowColumn>
<TableRowColumn>{purchase_date}
</TableRowColumn>
<TableRowColumn>${item_price}</TableRowColumn>
<TableRowColumn><a href={item_link}>{item_link}
</a></TableRowColumn>
<TableRowColumn><EditIcon class="grow:hover"
onClick={() => {this.handleClickEdit(row)}} />
</TableRowColumn>
<TableRowColumn><TrashIcon onClick={() =>
{this.handleClickRemove(id);
}}/>
</TableRowColumn>
</TableRow>
// END TABLE ROWS
);
});
<div>
{/* FORM FOR ADDING EXPENSES(DATA) */}
<form id="expenseForm">
<h3>
Add a new <br />
expense
</h3>
<input
type="text"
id="fname"
name="fname"
placeholder="Item description"
onChange=
{this.handleChange('item_description')}
/>
<br />
<br />
<input
type="text"
id="lname"
name="lname"
placeholder="Item price"
onChange={this.handleChange('item_price')}
/>
<br />
<input
type="text"
id="lname"
name="lname"
placeholder="Item link"
onChange={this.handleChange('item_link')}
/>
<br />
{/* END FORM */}
<RaisedButton
id="expSubmit"
label="Submit Expense"
primary={true}
style={style}
onClick={this.handleClick}
/>
{/* TABLE TOTAL KEEPS CURRENT TOTAL OF PRICE COLOUMN */}
<h1>Total:</h1>
<br />
<h3>$748.93</h3>
</form>
{/* TABLE HEADERS */}
<Table>
<TableHeader>
<TableRow>
<TableHeaderColumn>Item
description</TableHeaderColumn>
<TableHeaderColumn>Purchase
Date</TableHeaderColumn>
<TableHeaderColumn>Item
Price</TableHeaderColumn>
<TableHeaderColumn>Item
Link</TableHeaderColumn>
<TableHeaderColumn>Edit
entry</TableHeaderColumn>
<TableHeaderColumn>Delete
entry</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody>
{tableRows}
</TableBody>
</Table>
</div>
);
}
return (
<div>
<Nav />
{content}
</div>
);
}
}
// this allows us to use <App /> in index.js
export default connect(mapStateToProps)(ExpenseTable);
The default material-ui table doesn't have the feature of in-line editing. You will have to write your own wrapper to achieve this which can be time consuming. To overcome the time issue we can use some good existing libraries.
Here's one powerful library from DevExtreme for the same which i used, but go through their licensing before you take the final decision to use.
https://devexpress.github.io/devextreme-reactive/react/grid/demos/featured/controlled-mode/