Full stack components in Remix - reactjs

In this video, Ryan creates a newsletter signup form. The code for both the frontend and the backend are in the same file, which is an awesome Remix feature. However, something like a newsletter signup form might be on several pages of a website/app.
If you merely import the newsletter component on the homepage, for example, it will try to post to the homepage, which doesn't have an action() that handles that data submittion.
<Form replace method="post" aria-hidden={state === 'success'}>
<h2>Subscribe!</h2>
<p>Don't miss any of the action!</p>
<fieldset disabled={state === 'submitting'}>
<input aria-label="Email address" aria-describedby="error-message" ref={inputRef} {...formDefinition.inputs.email.validationAttrs} name="email" placeholder="you#example.com" defaultValue={actionData?.fields?.email} />
<button type="submit">{state === 'submitting' ? 'Subscribing...' : 'Subscribe'}</button>
</fieldset>
<p id="error-message">{state === 'error' ? actionData?.message : <> </>}</p>
</Form>
We can fix this by adding action="/newsletter" to the <Form>:
<Form replace method="post" aria-hidden={state === 'success'}>
But then, when you submit the form, you'll actually navigate to the /newsletter route, because data mutations, including form submitions are modeled with navigation. To fix this we could replace <Form> and useActionData() with const fetcher = useFetcher(), <fetcher.Form> and fetcher.data.
But now we lose the ability to merely reset the newsletter form with a simple <Link to=".">Start over</Link>.
Is this the right way of creating and sharing full stack components across routes in Remix?

Related

How to use <Link> and <Button> in React

I'm currently using React.
My code uses useForm in the react to perform validation.
I want to go to another page when a user enters a nickname and presses the Enter key or clicks the Button.
Now my code is to put the <button> tag inside the <Link> tag, and it will not complete the validation and will immediately go to another page.
In the opposite case, my code does not go to another page. Help me :(
<form onSubmit={handleSubmit(onValid)}>
<input
{...register("nickName", {
required: true,
minLength: {
value: 2,
message: "error",
},
})}
className="join-input"
placeholder="nickname here"
/>
<div className="error-message">{errors?.nickName?.message}</div>
<Link to={"/office"}>
<button className="go-btn bttn-material-flat">Enter</button>
</Link>
</form>
You should handle the linking to the new page in your handleSubmit function e.g handleSubmit(onSubmit) and then:
const onSubmit = () => {
// programatically link here, implementation differs depending on what framework you are using.
};
<form onSubmit={handleSubmit(onSubmit)}>
...
</form>

Can't get formik validation to work with react-router

Good morning,
I want a react-router link to forward ONLY when the form has no errors, unfortunately the code that's below does forward before the form is even validated so no error shows up and so on. Can you please guide me on how to fix this, so that the form is validated and if it has no errors, the link forwards further?
Here's my code:
<Link to={this.props.formikProps.isValid?'mainMenu': 'itemDetail'}>{!this.props.disabled && <button className="buttons-panel__button" type="button" onClick={() => (this.props.handleSubmit(false), this.props.formikProps.isValid? '' : this.onValidationFail())} >Save</button>}</Link>
The method is(it's basically only to display a popup error, and it's working):
onValidationFail() {
setAlertMessage("You need to fill all mandatory fields")
}
So far the best I've achieved is that it works as intended in every case, but if the form hasn't been touched at all. I've tried several different ways, including formValidate from formik, for some reason it didn't work too.
I'm looking forward to some tips/hints/solutions on the case.
I think that your solution looks bit overcomplicated. Maybe just use simple form submit, and after form submission redirect user to route.
It would look like that:
import { Formik, Form, Field } from 'formik'
class MyForm {
render() {
return (
<Formik
initialValues={...}
onSubmit={() => {this.props.history.push('/go-somewhere')}}
>
<Form>
<Field name="firstName" type="text" />
<button type="submit">Submit</button>
</Form>
</Formik>
)
}
}

Adding functionality to a form in a React functional component

I have a form component that when submitted uploads a photo to my server using multer and then to an S3 bucket on AWS in a single upload route. I want to add an extra line of functionality that stores the photo key from S3 in state on the front end which I will then store with the user object in the database so the photo is always associated with the user (profile picture).
The form sends properly as currently set up using the action parameter, but I'm not sure how to implement the submitHandler function into the form properly. If I remove the action parameter from the form it breaks, if I use onSubmit it also seems to break. I'm sure this is a really easy one but I can't figure it out. Any help much appreciated.
import React from 'react';
import axios from 'axios';
//setProfilePhoto is the set method for state from props
const UploadForm = ({setProfilePhoto}) => {
const submitHandler = async (e) => {
e.preventDefault();
const result = await axios.post('http://localhost:8000/api/upload');
console.log(result.key)
setProfilePhoto(result.key)
}
return(
<form action="http://localhost:8000/api/upload" encType='multipart/form-data' method='POST'>
<input type='file' name='photo' />
<input type='submit' value="submit" />
</form>
)
}
export default UploadForm;
try to take this line and delete it. <input type='submit' value="submit" />
Replace it with a button.
Then add the onSubmit handler to the form tag, not the button.

passing down functions correctly for onSubmit and handleChange on a form

I am trying to handle navigation around my application. I want to include a link back to the form component from displayweather using conditional statements. I can get the form to show correctly, however when you press submit on the form it just refreshes back to the Form under App
I am trying to pass the functions down from App to Navbar which holds a second instance of the Form
However, using react dev tools the functions are still showing as undefined?
Here is a gist of the code:
https://gist.github.com/dhuang612/6c683b6ccd0ce6299631db76ed76ccd7
The two functions I am trying to hand down from App
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
//handles submit on the form and runs the api call
onSubmit = e => {
e.preventDefault();
this.fetchWeatherData();
this.setState({ fetchedweatherdata: !this.state.fetchedweatherdata }, () =>
console.log(this.state.fetchedweatherdata)
);
};
this is how I am trying to pass the functions down to this second instance of form
<DisplayWeather
currentweather={this.state.currentweather}
currentforecast={this.state.currentforecast}
currenttime={this.state.currenttime}
weatherIcon={this.state.weatherIcon}
hourlyWeather={this.state.hourlyWeather}
/>
<Navbar fetchedweatherdata={this.state.fetchedweatherdata}>
<Form
onChange={this.handleChange}
{...this.state}
onSubmit={this.onSubmit}
/>
</Navbar>
and this is how I am trying to call the functions on the form
return (
<div>
{!this.state.fetchedweatherdata ? (
<div>
<button onClick={this.resetState}>return to form</button>
</div>
) : (
<Form onChange={handleChange} {...this.state} onSubmit=
{onSubmit} />
)}
</div>
I am not getting any error messages, the screen refreshes and reloads the origin Form under App, I would like the application to re-send out the api request and show current weather for the new location. Please see gist to see all the code together.
Thanks for any help!
The issue was that you did not pass nothing to Navbar itself.
I did put a codesandbox up for you, please, for the future question if there is more code included try to do the same with codesandbox, this can save lifes!
Here is the mentioned sandbox

Is there a way to get access to redux-form errors from inside the Container with reduxForm V7?

Previously, in Redux-Form V5, I was accessing errors as email.error and password.error from inside the render function.
In V7, my CSS is giving me grief to do it this way:
const renderInput = (field) => {
const { meta: { touched, error } } = field
return (
<div>
<input type={field.type} placeholder={field.placeholder} {...field.input} />
<div className="inputErrorText">
{touched ? error : ''} // this doesnt work due to my CSS
</div>
</div>
)
}
// ...inside the Container:
<div className="ui left icon input">
<i className="user icon"></i>
<Field
name="email"
component={renderInput}
type="text"
placeholder="Enter your Email"
/>
</div>
// Below is the old way I was doing it
{/*email.touched && email.error && <div className="inputErrorText">{email.error}</div>*/}
If I render the errors via renderInput(), the CSS gets mangled due to what looks like some position relative+absolute reasons, so I need the errors to render below that last div at the bottom of my code above.
How can I deal with rendering the field errors outside renderInput() and/or elsewhere in the <form>. I think my question centers around the field input parameter of renderInput(). How to access it inside the Container? Is it available on this.props anywhere?
I just got it working. I found this source: Redux-form 6.0.0 access error outside Field component which illustrates a way to use the Fields component of redux-form V6 & 7.
I wasn't able to get it working as shown there. I'm not entirely sure at this point, but it was throwing all kinds of "cannot read property touched of undefined". I tried many combinations of if (!fields) return else destructure, and about 20 different combinations and I just couldn't get it to iterate through using Object.keys(). I was able to console log easily fields.email.input and see everything, but as soon as I started digging into meta, it started throwing undefined even while it was logging the values sometimes. The weird thing is untouched was always false not undefined, so it was fairly infuriating, but I digress.
Another person may have more luck with that above solution. Fields is definitely what a person wants to use to gain control over errors in Redux-form V6-7.
I did however, get it to work like this:
import { reduxForm, Field, Fields } from 'redux-form'
const fieldNames = [
'email',
'password'
]
const renderInput = (field) => {
return (
<input type={field.type} placeholder={field.placeholder} {...field.input} />
)
}
// inside the Container (class methods)
renderEmailError({ email }) {
const { touched, error } = email.meta
if (touched && error) return <div className="inputErrorText">{error}</div>
return null
}
renderPasswordError({ password }) {
const { touched, error } = password.meta
if (touched && error) return <div className="inputErrorText">{error}</div>
return null
}
// Inside render function
<div className="field">
<div className="ui left icon input">
<i className="user icon"></i>
<Field
name="email"
component={renderInput}
type="text"
placeholder="Enter your Email"
/>
</div>
<Fields names={fieldNames} component={this.renderEmailError} />
</div>
x
I was not able to get it working with the "standard" implementation using Field because my CSS simply does not allow for a div to be placed along with the input. I could not alter the CSS because of the way it places the icons on top of the input fields.
I experimented with moving the entire 'fieldset' into the renderInput() function, but I became equally stuck because of the onClick handler when you click the eyeball to expose the password (UX substitute for passwordConfirm field). I was not able to update the component state from outside the class, nor was I able to call the function properly from outside the class.
Additionally, I also learned that you cannot manipulate an input's type if it is password using document.querySelector('input').type because it is a security risk.

Resources