How to change page in a Promise using React - javascript

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.

Related

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.

next/link losing state when navigating to another page

I am developing a library Next.js application. For the purposes of this question, I have two pages in my application: BooksPage which lists all books, and BookPage which renders details of a book. In terms of components, I have a <Books /> component which renders a <Book /> component for every book in my library database.
Here are my components:
Books.js:
function Books({ books }) {
return (
<>
{books.map(book => (
<Book key={book.id} book={book} />
))}
</>
);
}
Book.js:
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: false };
}
like = () => {
this.setState({ liked: this.state.liked ? false : true })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}
Problem:
Say that I am on page BooksPage. When I click the like button of a <Book /> the icon color toggles properly in the frontend and the like is successfully added or removed in the backend. When I refresh BooksPage all the state is maintained and consistent.
The problem arises when I like something on BooksPage and then immediately navigate to BookPage without refreshing using next/link. There the like button is not toggled consistently and the state from BooksPage is lost. Notice that if I hard-refresh the page everything goes back to normal.
Slow solution: Do not use next/link.
Replace
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
with
<a href={`/books/${book.slug}`}>{book.title}</a>
Fast solution: Keep using next/link?
Is there a way to use next/link and maintain state when navigating to another pre-rendered route?
TLDR: Any time the button needs to be changed, the API must change data, and it must update the closest parent's local state to update the button's appearance. The API will control all aspects of local state. You can't update local state unless an API request is successful. Therefore, the client and API are always 1:1.
The Button component in Book.js should NOT be maintaining its own state separately from the API data; instead, wherever you're fetching book data from the API, it should also be controlling the button's state (and its appearance). Why? Because with the current approach, the API request can fail, but the client will still update. As a result, the API and client may no longer be 1:1 (client shows liked, but API still shows that it's disliked/unliked).
In practice, you'll have a parent container that acts like a state manager. It fetches all relevant data, handles updates to the data, and displays the results using stateless child components. Any time a child component needs to be updated (such displaying a "like" or "dislike" button based upon a button press), it must first make an API request to change the data, then the API must respond with relevant data to the update the state used by the child:
Alternatively, if this component is reusable, then you'll conditionally render it using this.props.book (which comes from a parent container) or the child component must request data from an API to update its own local this.state.book. Same as the above diagram, the API requests control all aspects of state changes:
There's yet another approach that is the same as the diagram above, but it uses the child local state regardless of the parent's state. This child state will only be updated by its own class methods. This introduces a bit more complexity because you have to make sure that any parent state changes won't rerender the child component with stale data. Ultimately, which approach to take is up to you.
On a side note: This question doesn't make much contextual sense as libraries don't render pages nor do they attempt internal page navigations. Instead, they offer reusable components that can be utilized by one or many NextJS project pages or components.
You need to use Model.refresh_from_db(...)--(Django Doc) method to retrieve the updated value from the Database
class DeleteLikeView(APIView):
def post(self, request, book):
book = get_object_or_404(Book, id=book)
print(book.num_of_likes)
like = Like.objects.get(user=request.user, book=book)
like.delete()
book.refresh_from_db() # here is the update
print(book.num_of_likes) # You will get the updated value
return ...
Pass the liked property of a book somehow through the API. Then, pass that prop down from the Books component to the Book component.
Add a componentDidUpdate() method to your book component.
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: this.props.liked };
}
componentDidUpdate(prevProps) {
if (this.props.liked !== prevProps.liked) {
this.setState({
liked: this.props.liked,
});
}
}
like = () => {
this.setState({ liked: !this.state.liked })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}
In DeleteLikeView class you get book object. it retrieved from DB and saved in book variable.
when you deleted like object num_of_likes attribute has been updated in DB but your variable still consists previous object. after like.delete() command you should get object again and print num_of_likes att. It is as your expected.

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

(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.

Can I put information from an API to another page?

The title is the best I can really phrase it, however, heres my Microsoft Paint version of what I'm trying to do. If you guys know how to do it feel free to reply.
Heres what it is doing
Heres what I want it to do
Sorry for the bad drawings not the most artistic.
I'm not sure whats the use case you are trying to solve, but I will assume that it is something similar to how search could work. ie: You enter something in input fields, push submit button -> hide form you are currently in and show results in next page.
Pseudo code could look like this
class Search extends React.Component {
state = {
query: "",
results: null
}
handleOnChange = e => {
this.setState({query: e.target.value})
}
formSubmit = () => {
//do your request to api
.then(response => this.setState({results: response}))
}
render() {
return(
this.state.results === null ? (
<form>
<input type="text" value={this.state.query} onChange={this.handleOnChange}/>
<button type="submit" onClick={this.formSubmit}>Submit</button>
</form>
):(
<div>{this.state.results}</div>
)
)
}
}
React basically is a single page application so one way in doing what you want is to use react-router-dom when the submit button is clicked, you send the form data to the backend and then dispatch a new route that will display the component that has the API data. inside the componentDidMount life cycle of the component that will display the api data you make a fetch to get the data.

How to render results of search in another component in React?

I am a beginner in React and using Webpack to build into a bundle.js and display.
My need is to provide some search forms and accordingly display result below search form. So, for modularizing it, I have create a parent component containing both search and result view components.
Now that I have designed a form and written form onSubmit event handler, how should i proceed to render API results (dummy json for now) in the result component. I am attaching a brief pic of my intention for your reference.
Here is my solution based on my comments above: https://codesandbox.io/s/q85oq0w10q
Create an HOC that will hold the state of your app, then your two children are merely used for rendering purpose and can be made pure functions
import React from 'react';
import { render } from 'react-dom';
const Result = ({results}) => {
return results.map((r, i) => <div key={i}>{r}</div>);
}
const Search = (props) => {
const {
searchQuery,
onChange,
search
} = props;
return <div>
<input
type="text"
value={searchQuery}
onChange={onChange}
/>
<button onClick={search}>Search</button>
</div>;
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
searchQuery: '',
results: []
}
this.onSearchQueryChange = this.onSearchQueryChange.bind(this);
this.onSearch = this.onSearch.bind(this);
}
onSearchQueryChange(e) {
this.setState({searchQuery: e.target.value});
}
onSearch() {
// Simulate AJAX call
setTimeout(() => {
this.setState({results: [0, 1, 2, 3, 4]});
}, 1000)
}
render() {
const {results, searchQuery} = this.state;
return <div>
<Search
searchQuery={searchQuery}
onChange={this.onSearchQueryChange}
search={this.onSearch}
/>
<Result results={results} />
</div>;
}
}
I believe this is what you are looking for. Worked example fiddle
So the idea is to keep your result in Container component and pass it down to Result component. Also Container component should pass a callback function to your Search component and it will be triggered with a API result from the Search component.
Also you may want to take a look at Redux. This is a library for managing your app state. With Redux it can be achieved in more easiest way.
Hope it will help you.
In my opinion if you are new in React. You should learn first using React. Because I see that a lot of people use Redux(or any other app state handler) as a storage for any data.
Your case is actually very good example to learn two of the basic ideas: if children need similar thing, parents should handle it and params go down and callbacks go up.
So all your logic has to be in Container Component, make callback of http request function, with update of state(setState) after resolving response and send to Search Component. onSubmit call that callback, and send data to Result Component.
So you no need of additional library(maybe for http request).
1 Class component(Container Component). 2 Probably stateless functional components(Search Component & Result Component).

Categories