Next.js, how to submit a form to another page? - javascript

(Next.js) I have a GET form on one page. I want to submit it to another page. I know I can set the action property to the other page. That works. However, it does a page reload instead of just rendering the new page; the same as would happen if you had a link on the page without wrapping it in a Link component.
I could catch the submit event, build a query, and push it onto the router. But that seems like a lot of extra work for something I assume has already been figured out.
Any ideas how to do this without reinventing the wheel?
<form method='get' action='/search'>
<input name='q' placeholder='Search' arial-label='Search' />
</form>

I ended up catching the submit event and pushing a URL onto the router.
import {useState} from 'react'
import {useRouter} from 'next/router'
const preventDefault = f => e => {
e.preventDefault()
f(e)
}
export default ({action = '/search'}) => {
const router = useRouter()
const [query, setQuery] = useState('')
const handleParam = setValue => e => setValue(e.target.value)
const handleSubmit = preventDefault(() => {
router.push({
pathname: action,
query: {q: query},
})
})
return (
<form onSubmit={handleSubmit}>
<input
type='text'
name='q'
value={query}
onChange={handleParam(setQuery)}
placeholder='Search'
aria-label='Search'
/>
</form>
)
}

Based on the Next.js' routing system and Router API:
The Next.js router allows you to do client-side route transitions between pages, similarly to a single-page application. A special React component called Link is provided to do this client-side route transition.
Router.push also handles client-side transitions, and this method is useful for cases where next/link is not enough.
So it seems that you can only perform client-side transitions by using any of those two ways.
Using the form above will trigger a behavior as described by MDN docs for a form submission, as none of the above rules applies:
...The server then responds, generally handling the data and loading the URL defined by the action attribute, causing a new page load (or a refresh of the existing page, if the action points to the same page).
I also found another reference close to your question in Next.js' issues board, where the preferred method to follow, was the one you've also described as a solution.

Related

Navigate with page reload using react router

My application uses a Laravel backend and a React frontend. Ordinarily, I would use e.preventDefault() so that there are no page reloads. The issue I am having is that if I login or logout without a page refresh, it causes a CSRF mismatch on future requests. So I'm okay with the page refresh. However, when I remove e.preventDefault(), the form data becomes appended to the URL. The best solution I have been able to come up with is this:
let csrfToken = document.head.querySelector('meta[name="csrf-token"]').content;
const navigate = useNavigate()
const handleLogin = async (e) => {
e.preventDefault()
axios.post('login', {email, password}, {
'X-CSRF-TOKEN': csrfToken
})
.then(() => {
setLoginStatus(true)
navigate('/')
window.location.reload();
})
}
This essentially works, but I don't like it. What happens is that the view is updated with the Component at '/,' and then the page refreshes. It just looks weird. All I want is for the Component at '/' to be rendered upon the form's submission with a page reload. Keeping in mind my problem with removing e.preventDefault(), where the form data gets added to the URL.
I couldn't realize this problem. But I think you can use useEffect and useState together.
For example: When the login operation finishes, set the data which will be gained to state. And make dependency with useEffect and that state. So if the state change the component automaticaly re-render beacuse of the useEffect. In this case no reload needed.

calling setState only once inside of useEffect--is there a better method?

In my react app I use the following pattern quite a bit:
export default function Profile() {
const [username, setUsername] = React.useState<string | null>(null);
React.useEffect(()=>{
fetch(`/api/userprofiles?username=myuser`)
.then(res=>res.json())
.then(data => setUsername(data.username))
},[])
return(
<div>
{username}'s profile
</div>
)
}
When the page loads, some user data is fetched from the server, and then the page updates with that user data.
One thing I notice is that I only really need to call setUsername() once on load, which makes using state seem kinda excessive. I can't shake the feeling that there must be a better way to do this in react, but I couldn't really find an alternative when googling. Is there a more efficient way to do this without using state? Or is this the generally agreed upon way to load data when it only needs to be done once on page load
Without using any external libraries, no - that is the way to do it.
It would be possible to remove the state in Profile and have it render the username from a prop, but that would require adding the state into the parent component and making the asynchronous request there. State will be needed somewhere in the app pertaining to this data.
The logic can be abstracted behind a custom hook. For example, one library has useFetch where you could do
export default function Profile() {
const { data, error } = useFetch('/api/userprofiles?username=myuser');
// you can check for errors if desired...
return(
<div>
{data.username}'s profile
</div>
)
}
Now the state is inside useFetch instead of in your components, but it's still there.

React useEffect() hook highly affects SEO

I have a static website made with react that requests data from the backend in the useEffect() hook:
export default const App = () => {
const [data, setData] = useState("");
useEffect(() => {
server.get().then(data => {
setData(data)
})
})
return(
<title>{data}</title>
<h1>{data}</h1>
)
}
However, when Bing crawls the webpage, the following problem occurs:
Bing Screenshot:
<title></title>
<h1></h1>
How can I solve this issue?
React isn't used for static sites. If you'd like to have better SEO and server-side rendering you can use nextjs.
The way your app is setup currently will only return some HTML with and empty body to a GET request to / (which is what I suppose crawlers like the one you mentioned use) and starts rendering components after the JavaScript is loaded.
But if you decide on a server-side rendering approach, whenever a request is made to your app the server will first render the app on it's side and the return an HTML string with the rendered components.
Did you check if your server.get() is returning some data? I can't see any url here, so maybe it's actually returning nothing.
Even so, maybe you forgot to pass the second argument of useEffect, which is an array of arguments, which this hooks uses to trigger itself. For example, if you want to trigger only once, when component is mounted, you need to pass [] as second argument of useEffect.

Embed custom form twice, one instance open and another is close with React js?

How should i keep custom form twice, one instance open and another is close on the same page that is built with React js?
I have created one react app that is having form functionality and added it to our web page. Now I want an another instance of this app to have same functionality but as a open form in my page. How could I achieve this? Any Suggestion or link would be helpful regarding having multiple instance of a same react app form, if provided.
It's a bit tricky but can be done.
GenericForm.js (your generic form component)
const Form = (props: Props) => { ... }
const createReduxForm = reduxForm({ validate }) // validate is optional - just to show you can pass other stuff as usual
const ReduxForm = createReduxForm(Form)
export default ReduxForm
The takeaway from above is that you're declaring a Form without a form name.
View where the generic form gets used:
import GenericForm from '../GenericForm' // adjust for your paths
const View = (props) => {
return (
<div>
<GenericForm onSubmit={...} form="Form1" />
<GenericForm onSubmit={...} form="Form2" />
...
</div>
)
}
This exploits the fact that redux-from uses the form name to connect/update to the store. The form prop is the key to the redux store. So by passing form name dynamically, you can reuse the same generic form in a single view.
So that's the hard part. The easy part is keeping one open and one closed - you can use an accordion control or just some custom logic to show hide forms on the page for that.

How to change page in a Promise using React

I'm using Reactjs. I have a form that will populate my database onSubmit with just a name property. Assuming inserting data is success, How do I jump to back to my landing page in the promise? my landing page url is a simple '/'. or should i jump back to the landing page somewhere else and not in the promise.
const React = require('react')
const axios = require('axios')
class AddNewRecordLabel extends React.Component {
constructor (props) {
super(props)
this.state = ({
artists: []
})
this.onSubmit = this.onSubmit.bind(this)
}
componentDidMount () {
axios.get('http://localhost:5050/recordLabels')
.then((res) => {
this.setState({
artists: res.data
})
})
}
onSubmit (e) {
e.preventDefault()
if (!this.refs.name.value) {
console.log('fill in the name input')
} else {
var object = {
name: this.refs.name.value
}
axios.post('http://localhost:5050/recordLabels', object)
.then((res) => {
//change here
})
}
}
render () {
return (
<div>
<h3>add new record label</h3>
<form onSubmit={this.onSubmit}>
<label>
<input type='text' ref='name' placeholder='name'/>
</label>
<br></br>
<button type='submit'> Add Record Label </button>
</form>
</div>
)
}
}
module.exports = AddNewRecordLabel
Typically, you would create a library using a flux pattern which uses dispatchers, actions and stores to control your components. If you want to save a lot of time, there are libraries using the flux pattern out there such as react-redux and react-flux.
I have used a home grown flux pattern before. I'm using redux now which is fairly easy to use as well as pretty quick to develop with from my personal experience. There's great documentation on it and a lot of support from the community.
If you want to keep it simple, you might want to rethink your strategy such as returning message that either replaces the form giving them options such as going back to the home page or even leaves the form so they have an opportunity to add another record label. You would also want to check to see if there was an error and show some sort of message stating why it was unsuccessful based on the response. If you want to go back to the home page you could simply add this to your promise...
window.location.href = '/'
...but ideally, you would want to move your service calls to another file and return responses, then act on those responses in your component accordingly, but the general recommended approach is to do it by dispatchers and listeners and update your state or props within this component.

Categories