How to submit a form in React Native - javascript

I feel crazy asking this question here, but I can't find any good tutorials for how to submit a form and capture the data for RN. Everything I do find is someone pushing a library "just npm install react-native-form-genie-magic-box and call it in your project"...
but I just want to know - How to submit a form in vanilla React Native.
Sample code:
AuthContainer
class AuthContainer extends Component {
render() {
const { errorMessage, handleLogin } = this.props
return (
<Login
errorMessage={errorMessage}
onLoginClick={(e) => handleLogin(e)}
/>
)
}
}
.....
const mapDispatchToProps = (dispatch) => {
return {
handleLogin: (e) => {
e.preventDefault()
const form = e.target
const data = serialize(form, {hash: true})
const creds = { email:data.email, password: data.password }
dispatch(loginUser(creds))
},
}
}
Login
import { Container, Content, Form, Item, Input, Label, Button, Text } from 'native-base';
....
const Login = ({errorMessage, onLoginClick}) => {
return (
<Container>
<Content>
<Form >
{errorMessage &&
<Text>{errorMessage}</Text>
}
<Item floatingLabel>
<Label>Email</Label>
<Input
type="email"
name="email"
/>
</Item>
<Item floatingLabel last>
<Label>Password</Label>
<Input secureTextEntry={true} />
</Item>
<Button onPress={onLoginClick} ><Text>Sign in</Text></Button>
</Form>
</Content>
</Container>
)
}
Question: How can I just capture the submitted email and password in AuthContainer's handleLogin function?

On the <input you need to add something like this, example:
<Input onChangeText={(text) => this.setState({username: text})} value={this.state.username}
And when you use the onPress function you just need to get the this.state.username and use it when you want.
I don't usually do a function that handle the Login or something in other .js so you need to pass the this.state.username to the page that handles it.
What i usually do if I really need to pass something to other page is using GLOBALS, example:
// globals.js
module.exports = {
username: '',
};
And then to use the globals.js
// import the globals.js
GLOBAL = require('./globals');
<Input onChangeText={(text) => this_onButtonPressed(text) value={this.state.username}/>
_onButtonPressed(text){
GLOBAL.username = this.state.username
// call function that handles it
}
And then on the page that handles it you need to import it again and just use GLOBAL.username.
If you didn't understand it tell me I will try to explain it better, I need to know if you want to handle the login on a different .js or it can be on the .js that has the Form (its easier like this)

Hi you can use the following url for form implementation in react.
https://github.com/davidkpiano/react-redux-form/issues/383

Related

Using jest to send text to an input field and update state

Working on a technical challenge again. I want to set up unit tests (don't have to, but I would like to).
Problem is when I try to use userEvent or fireEvent to send some text to an input field, nothing happens (my expect toEqual test fails. I noticed that when I commented out enough of my code to figure out what was going on, I got the error: TypeError: setUsername is not a function when I use userEvent to type some text into the input field.
I assumed this meant I would need to create a mock state (setUsername updates the username state). However, this didn't resolve the issue.
The component I am testing is a child of a parent component Index . So I also tried wrapping this child component under the parent component (the username state is passed down to the child from Index). This didn't resolve the issue.
Here's my code:
const one = <index>
<One/>
</index>
describe("Step One", () => {
it("Can input text", async () => {
render(one);
const mockSetState = jest.fn();
const component ={
setState: mockSetState,
state: {
username: "test"
}
};
const username = screen.getByTestId("username");
const email = screen.getByTestId("email");
await userEvent.type(username, "Emmanuel");
userEvent.type(email, "emmanuelsibanda21#gmail.com")
expect(mockSetState).toEqual({"username": "emmanuelsibanda21#gmail.com"});
// expect(email.value).toEqual("emmanuelsibanda21#gmail.com")
});
});
This is the code in Index:
const [username, setUsername] = useState('')
const [email, setEmail] = useState(null);
return (
...
{
steps === 1 ?
<One
username={username}
setUsername={setUsername}
steps={steps}
setSteps={setSteps}
setSelectedCountry={setSelectedCountry}
selectedCountry={selectedCountry}
setSelectedState={setSelectedState}
selectedState={selectedState}
setSelectedCity={setSelectedCity}
selectedCity={selectedCity}
email={email}
setEmail={setEmail}
profilePic={profilePic}
setProfilePic={setProfilePic}
/> ...
}
Here's the code from One:
export default function One ({username, setUsername, steps, setSteps, setEmail, email, profilePic, setProfilePic}) {
const url = 'http://localhost:3000/api/getPic';
function changeHandler(e) {
setUsername(e.target.value)
}
function emailChange(e){
setEmail(e.target.value)
}
...
return (
<>
...
<div className={styles.description}>
<Input
data-testid="username"
onChange={changeHandler}
placeholder="What is your full name"
value={username}
/>
<Input
data-testid="email"
onChange={emailChange}
placeholder="Please enter a valid email"
value={email}
/>
</div>
{
username && isEmailValid(email) === true?
<Button data-testid="next" onClick={ () => nextStep() }>Next</Button>
: null
}
</main>
</>
)
};
Any ideas

How we can combine Smart Form Component and ConnectForm component from react hook form docs?

I came across the following two form designing approaches in react-hook-form documentation.
1. Smart Form Component ref
To make actual form decluttered with separate Form component which will handle all the react-hook-form methods injection generically / transparently. So that actual form does not have to involve injection code on every component:
App.jsx
<Form onSubmit={onSubmit}>
<Input name="firstName" />
<Input name="lastName" />
<Select name="gender" options={["female", "male", "other"]} />
<Input type="submit" value="Submit" />
</Form>
Form.jsx
export default function Form({ defaultValues, children, onSubmit }) {
const methods = useForm({ defaultValues });
const { handleSubmit } = methods;
return (
<form onSubmit={handleSubmit(onSubmit)}>
{React.Children.map(children, child => {
return child.props.name
? React.createElement(child.type, {
...{
...child.props,
register: methods.register,
key: child.props.name
}
})
: child;
})}
</form>
);
}
2. Connect Form ref
When we are building forms, there are times when our input lives inside of deeply nested component trees, and that's when FormContext comes in handy. However, we can further improve the Developer Experience by creating a ConnectForm component and leveraging React's renderProps. The benefit is you can connect your input with React Hook Form much easier.
export const ConnectForm = ({ children }) => {
const methods = useFormContext();
return children({ ...methods });
};
export const DeepNest = () => (
<ConnectForm>
{({ register }) => <input {...register("deepNestedInput")} />}
</ConnectForm>
);
export const App = () => {
const methods = useForm();
return (
<FormProvider {...methods} >
<form>
<DeepNest />
</form>
</FormProvider>
);
}
Doubt
I am guessing how one can combine above two. I guess I will need a Form like component inside <ConnectForm> ... </ConnectForm>, something like this:
SubForm.jsx
// #NOTE: being deep nested component and not top level component
// SubForm may not have onSubmit() unlike earlier Form component
export default function SubForm({ defaultValues, children}) {
const methods = useFormContext(); //** #NOTE: instead of useForm() **
return (
// ** #NOTE: No <form> element as this is not top level element but deeply
// nested element **
{React.Children.map(children, child => {
return child.props.name
? React.createElement(child.type, {
...{
...child.props,
register: methods.register,
key: child.props.name
}
})
: child;
})}
);
}
Notice three #NOTE comments in SubForm code to find how it differs from earlier Form
Then I need to use this inside <ConnectForm> ... </ConnectForm>, something like this:
export const DeepNest1 = () => (
<ConnectForm>
{({ register }) => {
<SubForm>
<Input name="firstName" />
<Input name="lastName" />
//...
</SubForm>
}}
</ConnectForm>
);
Then I can have many such DeepNest components:
export const App = () => {
const methods = useForm();
return (
<FormProvider {...methods} >
<form>
<DeepNest1 />
<DeepNest2 />
//...
<DeepNestN />
</form>
</FormProvider>
);
}
Am I correct with my understanding of how these two patterns in stated in react hook form doc should be used and how they should be used if we have to combine them? Is my component hierarchy in line with react component nesting / designing philosophy? Or I made mistake somewhere? Or is there yet another better approach?
PS:
Above code is kind of pseudo code to explain how I imagine the ideal component nesting approach should be for complex forms. Its not directly runnable.
I am absolutely noob in react! (Developing from scratch for the second time)

Clear sub-form values on conditional hide of nested fields

Using React+Formik, I want to create a reusable component that we can use to conditionally show/hide nested subforms (of any complexity).
Every time it becomes hidden, we wish to clear the values so that those values don't get submitted.
Below, a simple hide/show component called OptionalFeature is shown.
const OptionalFeature = ({
toggle,
children
}) => {
if (toggle) {
return <div>{children}</div>
} else {
return null;
}
}
It can be tested by pasting into https://codesandbox.io/s/zkrk5yldz
But as you can see in the demo, making the children invisible does not clear their values. Ideally each child can define it's own clearValue behavior (the example is very simple, we want to have more complex nested forms).
What's the clean solution to clear the fullname field by extending OptionalFeature class in a generic, reusable way?
I already tried creating a cleanup function and calling it from OptionalFeature inside the if-block, but it does not seem very idiomatic.
// Helper styles for demo
import "./helper.css";
import { DisplayFormikState } from "./helper";
import React from "react";
import { render } from "react-dom";
import { Formik } from "formik";
// Generic reusable component to show/hide sub-forms
const OptionalFeature = ({
toggle,
children
}) => {
if (toggle) {
return <div>{children}</div>
} else {
return null;
}
}
const App = () => (
<div className="app">
<Formik
initialValues={{ email: "", anonymous: false, fullname:"" }}
onSubmit={async values => {
await new Promise(resolve => setTimeout(resolve, 500));
alert(JSON.stringify(values, null, 2));
}}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleSubmit
} = props;
return (
<form onSubmit={handleSubmit}>
<input
id="email"
placeholder="Enter your email"
type="text"
value={values.email}
onChange={handleChange}
/>
{/* This checkbox should show/hide next field */}
<div style={{ display: "white-space:nowrap" }}>
<label htmlFor="anonymous" style={{ display: "inline-block"}}>Anonymous</label>
<input
id="anonymous"
type="checkbox"
name="anonymous"
value={values.anonymous}
onChange={handleChange}
style={{ display: "inline-block", width: "20%"}}
/>
</div>
<OptionalFeature
toggle={!values.anonymous}
>
{/* Imagine this subform comes from a different file */}
<input
id="fullname"
placeholder="Enter your full name"
type="text"
value={values.fullname}
onChange={handleChange}
/>
</OptionalFeature>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
</div>
);
render(<App />, document.getElementById("root"));
Here is my existing approach, waiting for a better answer:
const OptionalFeature = ({
toggle,
onHide,
children
}) => {
if (toggle) {
return <div>{children}</div>
} else {
// useEffect needed because onHide function could trigger anything.
// Also to avoid calling multiple times.
useEffect(() => {
onHide();
}, [toggle, onHide])
return null;
}
}
Then later invoke a cleanup function onHide:
<Formik
initialValues={{ email: "", anonymous: false, fullname:"" }}
...>
{props => {
const {
values,
isSubmitting,
handleChange,
handleSubmit
} = props;
// to clean up all prop values inside the OptionalFeature
const clearFullName = () =>
{
values.fullname = ""
}
return (
//...
<OptionalFeature
toggle={!values.anonymous}
onHide={clearFullName} // using cleanup Function
>
<input
id="fullname"
placeholder="Enter your full name"
type="text"
value={values.fullname}
onChange={handleChange}
/>
</OptionalFeature>
);
}}
</Formik>
What I don't like here is that as the for becomes more complex with more OptionalFeatures or more elements nested inside the optional feature, it becomes quite hard to check whether all fields inside the nested optional form are being cleaned up or not. Also the properties of useEffect seem hard to test.
I would prefer some kind of nested subform such that I could write something like onHide={handleReset}, and this would be scoped only to fields inside the nested subform, without me having to define a custom handleReset function for that.

Form dialog don't set state

I have created a simple login function where once the user log in, he is redirect to another page. Then I wanted to change the login form with a form dialog. And the problem is here. The login dialog works, but when I enter the username and password, I'm not send to another page but to the same login page :/.
Here is the code:
Login.jsx:
class Login extends Component {
constructor(props) {
super(props);
this.state = {
islogged: false,
loginSettings: {
lUsername: "",
lPassword: ""
}
};
}
handleInput = (event) => {
let loginSettingsNew = { ...this.state.loginSettings };
let val = event.target.value;
loginSettingsNew[event.target.name] = val;
this.setState({
loginSettings: loginSettingsNew
});
};
login = (event) => {
let lUsername = this.state.loginSettings.lUsername;
let lPassword = this.state.loginSettings.lPassword;
if (lUsername === "admin" && lPassword === "password") {
localStorage.setItem("token", "T");
this.setState({
islogged: true
});
} else {
console.log("Erreur");
}
event.preventDefault();
};
render() {
if (localStorage.getItem("token")) {
return <Redirect to="/" />;
}
return (
<div className="Login">
<Dialog handleInput={this.handleInput} login={this.login} />
<p>Username: admin - Password: password</p>
</div>
);
}
}
Dialog.js:
export default function FormDialog() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open form dialog
</Button>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Login</DialogTitle>
<DialogContent>
<form onSubmit={this.login}>
<label>
<span>Username</span>
<input name="lUsername" type="text" onChange={this.handleInput} />
</label>
<label>
<span>Password</span>
<input name="lPassword" type="password" onChange={this.handleInput}/>
</label>
<Button onClick={handleClose} color="primary">Cancel</Button>
<Button type="submit" value="submit" color="primary">Login</Button>
</form>
</DialogContent>
</Dialog>
</div>
);
}
I also have created a sandbox of my code: https://codesandbox.io/s/react-login-auth-forked-r06ht
I thinks that the problem come from the form dialog that can't set the state of islogged of the login function. I tried quite a lot of things but nothing worked, so I would like to ask some help please.
I thank in advance anyone who will take the time to help me .
I notice that your Login component is a class, while the child FormDialog component is a function component using Hooks. There's nothing wrong with that, in itself - particularly if you started with an application using all class components and are slowly converting your components. But I detect some confusion here, because your function component seems to assume it's a class, in a sense. When you need to do things a bit differently.
Specifically, you reference this.login in your function component - but this is meaningless. login is passed in as a prop. In a class component, you would reference it as this.props.login. This doesn't work in a function component - but this.login isn't the substitute. Indeed this will tend to be undefined so this would even give an error. Even if not, it's not correct.
All you need to do is to make use of the argument to your function component - this is where the props object "lives" in a function component. You happen to ignore it by not using any arguments - but you don't have to do this.
So in short, what you need to do is:
replace export default function FormDialog() with export default function FormDialog(props)
replace this.login with props.login
repeat 2) for all other props which you have referenced using this
Ok all fixed. In your App.js you didn't have a home component to redirect to with this path="/". I've created a new component called home.js. Tidied the Routes for you.
<Route path="/login" component={Login} />
<ProtectedRoute path="/dashboard" component={Dashboard} />
<Route exact path="/" component={Home} />
Please check ur code sandbox https://codesandbox.io/s/react-login-auth-forked-24m8e?file=/src/App.js

Setting/Clearing input in Redux/React

I would like to know how one can wire in a clear field via redux when using react.
For instance, I have a form with some sample code.
<Field component={datewidget} id ="date" name="date" type="date" label="date" />
<button type="button"onClick={() => {(clearMyInput());}}>
</button>
The function clearMyInput is dispatched such that:
const mapDispatchToProps = (dispatch) => {
return {
clearMyInput: () => {
return dispatch(clearDate(datedata,value));
}
}
}
My question how can one clear the input field by simply clicking on the button and setting the value of the input to none.
For example in jquery, i can write something like this:
$("#button").click(function () {
$("#date").val("");
});
I would like to know how one can do this using redux forms in react.
In your Field's component, pass attribute value from your store and attach event handler to dispatch change.
<Field
component={datewidget}
id="date"
name="date"
type="date"
label="date"
value={this.props.fieldValue}
onChange={this.props.changeFieldValue}
/>
Then with dispatching clearDate function, you just need to set all form's values to ''. I recommend to look into Redux Form which do this all without boilerplate.
I do want to add as a supplement to the existing answer that there is built-in functionality for clearing a form:
<button type="button" onClick={reset}>
Clear Values
</button>
Notice the {reset} onClick action handler here. This comes out of the box with Redux-Form
import { reset, destroy } from 'redux-form'
//
//..code Block
render() {
const { resetForm, destroyForm } = this.props
return <ComponentName {...{ resetForm, destroyForm }} {...this.props} />
}
}
// #formName: for global use(in case if you render more than one form from this container..)
const mapDispatchToProps = dispatch => ({
resetForm: formName => dispatch(reset(formName)),
destroyForm: formName => dispatch(reset(formName)),
})
and now you call it from the component
const ComponentName = ({ resetForm, destroyForm }, ...props) => {
//..code Block
<button type='submit' onClick={() => resetForm('formName') && destroyForm('formName')} > Clear </button>
}
HAPPY CODING..

Categories

Resources