How to trigger requests with a button using React-query? - javascript

I have been trying to learn React-query but can't seem to trigger requests with my onSubmit event. Right now the code is sending the request with "washington" as the default parameter and printing it to the screen, and a new request also triggers with the onBlur event, and fetch the data if the city typed is valid.
The thing is that wish I could move this logic to the submit() function, treat the data on the input and only if the data is valid, proceed to make the request. This is the stackblitz where I reproduced the problem with a free apiKey: StackBlitz
This is the code:
import React, { useState } from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const Fetch = async city => {
let apiKey = '91b5ff77e9e7d1985a6c80bbbb3b2034';
const { data } = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
);
return data;
};
const Weather = () => {
const [city, setCity] = useState('washington');
const { data, error } = useQuery(['temperature', city], () => Fetch(city));
const submit = () => {};
return (
<div>
<form onSubmit={submit}>
<input onBlur={e => setCity(e.target.value)} type="text" />
<button type="submit">send</button>
</form>
{!data ? null : <div>{data.main.temp}</div>}
</div>
);
};
export default Weather;

You can also call setCity in the onSubmit event of the form, as the onSubmit event gets the complete submitted form in the submit event:
<form
onSubmit={(event) => {
event.preventDefault();
const city = new FormData(event.currentTarget).get("city");
// do validation here
if (isValid(city)) {
setCity(city)
}
>
<input name="city" type="text" />
<button type="submit">send</button>
</form>
make sure to give your input a name so that you can grab it from the form submit event.

You can use useMutation hooks. As what the documentation said mutations are typically used to create/update/delete data or perform server side-effects. For this purpose, React Query exports a useMutation hook.. This hooks will return an object that gives you a mutation function that you can use to trigger request based on user interactions.
const { mutate: renamedMutationFunction } = useMutation(newTodo => axios.post('/todos', newTodo)).
Then somewhere in your code, you can do:
const handleClick = () => { renamedMutationFunction(); //invoking the mutation }
EDIT
see #TkDodo answer for better solution. You can basically just re-set the city, and react-query will automatically refetch the data.

Related

Pass data from searchbar into backend function

The title says it all - I'm trying to send a word from a search bar to be consumed and processed in a backend function.
Any ideas on how this can be accomplished?
I'm using react and node/express for this
you also can use jQuery.The purpose of jQuery is to make it much easier to use JavaScript on your website. jQuery takes a lot of common tasks that require many lines of JavaScript code to accomplish, and wraps them into methods that you can call with a single line of code.
You can use the following method. The search term can be accessed in the backend from req.body. I have also used the debounce method so that the api won't be flooded if user continuously types in the search bar
import { useEffect, useState } from "react";
import debounce from "lodash.debounce";
import axios from "axios";
export default function App() {
const [text, setText] = useState("");
useEffect(() => {
if (text.length > 0) {
const payload = {
query: text
};
axios
.post("your_api_url_here", payload)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
}
}, [text]);
const update = (e) => setText(e?.target?.value);
const debouncedOnChange = debounce(update, 500); // delay to prevent bulk api call
return (
<div className="App">
<input placeholder="Search" type="text" onChange={debouncedOnChange} />
</div>
);
}
Whenever you decide it's the moment to send the value (either pressing a button or detecting a specific string) you can get the value of that element accessing document object and send it through a request to backend (maybe using axios)

Error: Form submission canceled because the form is not connected

Disclaimer re potential duplicates: There have been similar questions to mine and I think I have read pretty much all of those answers by now but none of them solved the issue for me.
I've got an app, frontend is React, and backend is a flask server. I'm having a major issue. The code seems to check out for both front- and backend, but after I click the submit button of a form, I get the error in the JS console: Form submission canceled because the form is not connected.
Now, there is only one button on my Form, and this is of type submit, with the handleSubmit handler in the form tag, so I don't think this is causing the issue.
I also added this config object to handle potential CORS faults but I'm not getting any CORS errors at least.
React Form Component code:
import React, { useState } from "react";
import axios from "axios";
import Button from "#material-ui/core/Button";
import { TextField } from "#material-ui/core";
import DisplayUpper from "./DisplayUpper";
function Form() {
const [inputText, setInputText] = useState("");
const [fetchedData, setFetchedData] = useState("");
const [isSubmitted, setSubmitted] = useState(false);
const handleSubmit = (e) => {
setSubmitted(true);
console.log("button clicked");
const config = {
headers: { "Access-Control-Allow-Origin": "*" },
};
axios
.post(
"http://localhost:5000/process",
{
inputText: inputText,
},
config
)
.then((res) => {
console.log("res", res);
setFetchedData(res.data);
})
.catch((er) => {
console.log(er);
});
};
return (
<div>
<form onSubmit={handleSubmit} method="post">
<label>
Enter Text :
<TextField
multiline={true}
variant="outlined"
name="inputText"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
/>
</label>
<Button variant="contained" color="primary" type="submit" name="Submit">
SUBMIT
</Button>
</form>
{isSubmitted && <DisplayUpper />}
</div>
);
}
export default Form;
Then, the app.py code:
#app.route('/process', methods=['POST'])
def result():
text = request.form.get('inputText')
upper_text = text.upper()
print(upper_text)
return upper_text
I set proxy: "http://localhost:5000" in package.json.
The browser is attempting to submit your form to the current URL via the method on the form when the button of type="submit" inside it is being clicked.
As you are handling the form submission with javascript, you should prevent the form's default behaviour:
const handleSubmit = (e) => {
e.preventDefault();
...
}
I got this warning when I had a button inside a form tag - the button click handler navigated away from the page
The default behaviour for a button inside a form is type="submit"
The warning disappeared when I changed the button to type="button"
In my case, I did not want the button to submit the form anyway - Otherwise if the button is meant to submit the form then use #ksav's answer

Data from API is not filled inputs fields. Formik

I create simple form on functional component ReactJS, with using Formik. Input fields my form should get data from API, when component mount. For this i'm using fetch and useEffect. After fetching, data from the API is retrieved but not filled until formik.handleChange is triggered.
If try to specify something in any input field, formik.handleChange is triggered, and all fields are immediately filled with data from the API (in my case, sould filled only email)
How i can triggered formik.handleChange when component will mount ?
import React, { useEffect } from 'react';
import { TextField, Button } from '#material-ui/core/';
import { useFormik } from 'formik';
import * as yup from "yup";
const validationSchema = yup.object({
Title: yup.string("Enter your Title").required("Title is required"),
email: yup
.string("Enter your email")
.email("Enter a valid email")
.required("Email is required"),
});
export default function Form() {
const formik = useFormik({
initialValues: {
Title: "",
email: ""
},
validationSchema: validationSchema,
onSubmit: async (values) => {
//...code of post function
//...code of post function
//...code of post function
}
});
useEffect(() => {
(async () => {
try {
let response = await fetch(
"https://run.mocky.io/v3/5be581aa-89d3-43e3-8478-7186633f8d16"
);
let content = await response.json();
formik.initialValues.email = content[0].email;
} catch (e) {
console.log(e);
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<form onSubmit={formik.handleSubmit}>
<TextField
fullWidth
id="email"
name="email"
label="Email"
value={formik.values.email}
onChange={formik.handleChange}
error={formik.touched.email && Boolean(formik.errors.email)}
helperText={formik.touched.email && formik.errors.email}
/>
<Button color="primary" variant="contained" fullWidth type="submit">
Submit
</Button>
</form >
);
}
Link to SandBox
In addition, I have a second way with using ReactState, but I think it way are lesspreffered but it has another error: After fetching, states fill my input fields, but fail validation. When i'm trying submiting data, to validation sending default values of react State (those that came before fetch)
Link to SandBox with useState way
You were on a good path. Reassigning values to formik instance will not help. Use setFieldValue to set value after fetching data (or simply setValues if you want to set all values):
formik.setFieldValue("email", content[0].email);
or:
formik.setValues(content[0]);
Here's a modified version of your Sandbox:
https://codesandbox.io/s/jolly-sun-uppr6?file=/src/App.js
Don't forget to set your formik to enable reinitialize - https://formik.org/docs/api/formik#enablereinitialize-boolean

How to make axios get request dynamically in react.js?

I am trying to make an axios get request in react.
Where the url will be dynamic.
The react code is:
class Players extends React.Component {
state = {
players: [],
};
componentDidMount() {
const fetchData = async () => {
const res = await axios.get(
"https://nba-players.herokuapp.com/players-stats/Aaron" {*I want to make this name dynamic and grab this name from a form */}
);
this.setState({ players: res.data });
};
fetchData();
}
render() {
return <div>{this.state.players.team_name}</div>;
}
}
The html form is:
<form id="form">
<label for="name">Type name</label>
<input id="name" type="text" />
<input type="submit" />
</form>
When the user types the name in the input and clicks on submit in this form,
then,It will be redirected to the react app and the axios get request will be https://nba-players.herokuapp.com/players-stats/ + The name user typed in that form
so that, I can get data in my react app accordingly to the name that the user typed in the form.
Here is my approach:
React code:
class Players extends React.Component {
state = {
players: [],
};
componentDidMount() {
const fetchData = async () => {
const res = await axios.get("https://nba-players.herokuapp.com/players-stats/" + window.location.href.split('/')[4]);
this.setState({ players: res.data });
};
fetchData();
}
render() {
return <div>{this.state.players.team_name}</div>;
}
}
HTML:
<form id="form">
<label for="name">Type name</label>
<input id="name" type="text" />
<input type="submit" />
</form>
JQuery:
$("#form").on("submit", function () {
$(this).attr(
"action",
"http://localhost:3000/" + $("input").val() //react project on port 3000
);
});
Is there are any better way to do it?
This is a very common use case. You want to have a state that is connected to an input field of an form. You want the state to represent the latest change of the input field and onSubmit of the form you want to take that state and use it, e.g. to catch something from a REST API.
Have a look at the official documentation of React: https://reactjs.org/docs/forms.html
It's a step to step tutorial on how to connect input to state.
Let me write down the most important parts:
Every input field that you describe in your render return jsx has an onChange callback function that you can use.
<input onChange={myCallbackFunction} />
This callback function will be called every time the value of the input field changes.
const myCallbackFunction = (event) => {
// standard web api change event
const newValue = event.target.value;
// update state based on input field changes
this.setState({ playerName: newValue });
}
Now you got your state updated based on the input field.
The last step is to also create a onSubmit callback function which triggers the axios call.
<form onSubmit={mySubmitFunction}>
const mySubmitFunction = (event) => {
// standard web api submit event
event.preventDefault();
// use state
const url = `https://nba-players.herokuapp.com/players-stats/${this.state.playerName}`
// TODO call axios with dynamic url
}

Modularizing code in React/Redux

The main question
I am used to using React with ES6 classes. I am also used to modularizing portions of code into separate functions. I am looking at the following example and trying to figure out how to put the value for onSubmit as a separate function.
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form
onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}
>
<input
ref={node => {
input = node
}}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
I have tried something like this:
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
function handleSubmit(e){
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => handleSubmit(e)}>
<input ref={node => {input = node }}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
But then of course it does not work as it does not recognize the input variable. I could pass the input variable to the function, but this does not seem like the right way to do it.
Question 2:
I am unfamiliar with what the following piece of code is doing:
let AddTodo = ({ dispatch }) => {
Where exactly is it getting dispatch from? Is the value of dispatch being passed into the anonymous function?
Question 3
The same with the following code:
<input ref={node => {input = node }}
Where is the value of node coming from and why is it being stored into the input variable?
Answer to Question 1
AddTodo is a React stateless functional component (SFC). It is also a function. Within the SFC is defined a variable input. In order for the handleSubmit callback to be able to make use of input, it is necessary that input be in the enclosing scope where handleSubmit is defined or input be passed as an argument to handleSubmit.
Thus, the following two implementations achieve the desired behavior:
const AddTodo = ({dispatch}) => {
let input
const handleSubmit = e => {
...
}
return (
...
onSubmit={handleSubmit}
...
)
and
const handleSubmit = (e, input) => {
...
}
const AddTodo = ({dispatch}) => {
let input
return (
...
onSubmit={e => handleSubmit(e, input)}
...
)
I highly recommend reading the following blog post by Kent Dodds, paying particular attention to the use of classes vs function closures.
Classes, Complexity, and Functional Programming
Answer to Question 2
The connect function from react-redux wraps the AddTodo component. The way in which connect is being called (with no second argument, or any arguments in this particular case) means AddTodo will receive a prop named dispatch.
To better understand how react-redux and the connect function it provides work, have a look at the documentation:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
Answer to Question 3
Refs are built into React. A function passed to the ref prop receives the underlying DOM element as an argument. In this case, the function passed to the ref prop stores a reference to the DOM element in the variable input. This allows the DOM element to be accessed and mutated later by the callback passed to onSubmit (i.e. handleSubmit). See the React documentation for more details on refs:
https://reactjs.org/docs/refs-and-the-dom.html

Categories