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
}
Related
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.
I am making a multi-stage form in react. The overall component dependency structure is as follows:
MainForm
SubForm1
SubForm2
SubForm3
The MainForm component has two states called step and formData, and methods called handleNext, handleBack which modify the state step. It also has a method called handleChange, which reads the value from input fields present in SubForm* and update the state formData, so that on clicking back and next the formData stays there until a final API call has been made on the last SubForm3. Upon which the MainForm component is unmounted. The MainForm uses switch case to render a particular SubForm using the state step as the decision variable.
I am passing the following to SubForms as props:
formData
handleNext
handlePrev
handleChange
In SubForm1 I have the following piece of code:
import React from 'react';
const SubForm1 = ({
formData,
handleNext,
handlePrev,
handleChange,
}) => {
const FormInput = ({ attr }) => <input name={attr} onChange={handleChange} value={formData[attr]} />;
return (
<FormContainer>
<form>
<input name='fullName' onChange={handleChange} value={field_values.fullName} />
<FormInput attr="employee_id" />
</form>
<button onClick={prevStep}>Back</Button>
<button onClick={nextStep}>Next</button>
</FormContainer>
);
}
The handleChange method captures the user input via the onChange event and upadates the corresponding field. It is declared in MainForm.jsx as:
// MainForm.jsx
import React, { useState } from 'react';
const MainForm = () => {
const [step, setStep] = useState(0);
const [formData, setFormData] = useState({ fullName: '', employee_id: '' });
const handleChange = (event) => {
event.preventDefault();
event.persist();
setFormData(prevState => ({
...prevState,
[event.target.name]: event.target.value,
}));
};
const handleNext = () => {
setStep(old => (old + 1));
};
const handleBack = () => {
setStep(old => (old - 1));
};
It works smoothly in the input field (fullName) - The value updates as the user types and the formData state remains intact on traversing through subforms.
But for the employee_id input, following happens:
A character is entered in the employee_id input field.
The employee_id input field immediately looses focus.
The user has to click on the employee_id input field again to gain focus (of course without which he/she cannot type into the field)
Go To step 1.
The state formData is however updated since on going to next/previous SubForm, the state formData remains intact.
I feel that it has something to do with how I have declared the FormInput component. I am not passing handleChange as a prop to it from SubForm1, but instead relying on the scope.
After googling a lot, I couldn't get an answer for my question, since search engines confused my question with answers relating to Component composition on any query containing 'declaring a component inside another component` etc.
PS. I am relatively new to react and javascript, so please feel free to suggest any better ways to implement the above.
Any help is highly appreciated.
sometimes event.persist(); is causing problem.
If you want your event.target.name and event.target.value then just create two variables which will store these values.
and remove event.persist().
just try with below code :
const handleChange = (event) => {
event.preventDefault();
// event.persist(); //comment out this line
const name = event.target.name;
const value = event.target.value;
setFormData(prevState => ({
...prevState,
[name]: value,
}));
};
Noob in ReactJS and Django here.
I have to model a website for a school project and we are using ReactJS for frontend and Django for backend. The app currently runs fine, but I encountered a problem to redirect my Search Button to the exact search page I need.
Here is what I want to do exactly:
User types in a string in the SearchBar. For example, types in: hello.
User presses the SearchButton.
The SearchButton will redirect the user to this EXACT path/url:
http://127.0.0.1:8000/search/?q=hello
Currently, this is what I have:
export class SearchBar extends React.Component {
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Search questions here"
id="query" name="/search/?q="/>
<input type="submit" value="Search"
onclick="onLoadConfigPress(document.getElementById(name).value)" />
</form>
);
}
}
What this redirects me to is:
http://127.0.0.1:8000/?%2Fsearch%2F%3Fq%3D=ss
Clearly, that is not equal to:
http://127.0.0.1:8000/search/?q=hello
Because the onClick does not accept special characters from parameter "name", which are the chars: / /? =
So, my question is, is there a way to encode this "name" parameter, something like stringify() or EncodeUIC() or another easier way to redirect the SearchButton to the exact url?
Thank you in advance
I believe you first have to read and learn how react works, so you can get better results and not run into that kind of issues
first of all, I do not think this is ok, do you have onLoadConfigPress function and name variable globally? where do they come from?
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Search questions here"
id="query" name="/search/?q="/>
<input type="submit" value="Search"
onclick="onLoadConfigPress(document.getElementById(name).value)" />
</form>
You can refactor that to
<form onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Search questions here"
onChange={this.handleChange}
/>
<input type="submit" value="Search" />
</form>
where handleSubmit and handleChange are methods of your class Component SearchBar
Below you can find and example of your component and its functionality in the react way of doing it
class SearchBar extends React.Component {
constructor (props) {
super(props);
this.state = {
search : '',
url : '',
redirect : false
}
// binding the context to the SearchBar
// because this functions will be executed in a
// different context
this.handleChange = this.handleChange.bind(this);
this.handleSearch = this.handleSearch.bind(this);
}
// Modify the search key of the component state
handleChange (e) {
this.setState({
search : e.target.value
});
}
// gets the user input
// and build the url based on it
handleSearch (e) {
e.preventDefault(); // prevent page reload
const { search } = this.state;
const url = `your_url/search/?q=${encodeURIComponent(search)}`;
console.log(url)
// What do you want to do?
// if fetch data based on the url
// then make an http called using fetch
// and update the state of your component
// using setState method
// if redirect (assuming your have a SPA and using react router dom)
// use
// this.setState({
// url,
// redirect : true
// });
// that will force your component to rerender
}
render () {
const { search, url, redirect } = this.state;
// if you are using react-router-dom just
// render a Redirect Component
// if (redirect) {
// return <Redirect to={url} />
// }
//
return (
<form onSubmit={this.handleSearch}>
<input
value={search}
onChange={this.handleChange}/>
<button type="submit">Search</button>
</form>
);
}
}
For encoding and decoding uri strings are:
encodeURIComponent(url)
decodeURIComponent(url)
https://www.w3schools.com/jsref/jsref_decodeuricomponent.asp
Hopefully that will help.
I've got the last.fm API working in my app, I'm grabbing related artists. So the idea is you search for an artist and it returns a list of related artists.
If I use 'onClick' it works perfect because it grabs the input value, but I want to use 'onChange' and it seems to returning the wrong results. I think it's because it's undefined at some stages or something!
Any ideas how to fix this?
// AJAX
import axios from "axios";
module.exports = {
fetchArtistCategory: (artist) => {
let key = "12345";
let encodedURI = window.encodeURI('http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=' + artist + '&api_key=' + key + '&format=json');
return axios.get(encodedURI)
.then((res) => {
return res
});
}
}
// App
import React from "react";
import {render} from "react-dom";
import Artists from "./components/Artists.js";
import Api from "./components/Api.js";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
artist: [],
}
this.searchArtist = this.searchArtist.bind(this);
}
componentDidMount() {
}
searchArtist() {
Api.fetchArtistCategory(this.searchArtists.value)
.then((res) => {
this.setState({
artist: res.data.similarartists.artist
});
})
}
render() {
return (
<div>
<Artists artistValue={this.state.artist} />
<input type="text" placeholder="Search artist" ref={(ref) => this.searchArtists = ref} onChange={this.searchArtist} />
<input type="submit"/>
</div>
)
}
}
render(<App />, document.getElementById("main"));
It's hard to diagnose your problem without knowing what you mean by "returning the wrong results". My initial guess is that you're thinking about onChange incorrectly.
onchange fires for every single text change. If I typed in "Bob Dylan" into your text field, onChange would fire once for each change as I type (eg. "B", "Bo", "Bob", "Bobe", "Bob"(delete e), "Bob "...). Thus you'll be firing a lot of API requests. This is fine for autocomplete, but requires rate-limiting, smart response ordering, etc., and is a different use case than yours.
It sounds like you only want to fire one API request once the user has completed their thought. To do this, you should try the onfocusout or onblur attributes -- as their names imply, they will fire when the user has completed their input and left the text field.
shouldn't you be passing an event through with the onChange handler?
searchArtist(event) {
Api.fetchArtistCategory(event.target.value)
.then((res) => {
this.setState({
artist: res.data.similarartists.artist
});
})
}
^ The only piece of code you should need to change if everything else if correct
And you need the ref unless I'm misunderstanding. You get the value of the input field as event.target.value inside of the event handler.
This is my component:
export default function ImageUpload({ onSuccess, children}) {
let value = '';
const onUpload = async (element) => {
element = element.target.files;
let response;
// Call endpoint to upload the file
value = element[0].name;
return onSuccess(response.url);
};
return <div>
<div >
{children}
<input type="file" id="upload" onChange={onUpload.bind(this)} />
</div>
<span>{value}</span>
</div>;
}
I would like to print inside the span the name of the file chosen from the user.
I use redux for the state, but this information is not something that belongs to the state.
This way does not work, could someone explain me why?
UPDATE
Since looks like there is no way to achieve this without state my question is about the best approach:
Use Redux also if I don't need this value outside the component or use the internal state of the component but ending to have both Redux and this state?
I can think about changing the approach, and transform your component to a class type component, something like that:
export default class ImageUpload extends Component {
constructor() {
this.state = {
value: ''
}
this.onUpload = this.onUpload.bind(this)
}
onUpload() {
let value = '';
const onUpload = async (element) => {
element = element.target.files;
let response;
// Call endpoint to upload the file
value = element[0].name;
this.props.onSuccess(response.url);
this.setState({ value: value })
};
}
render () {
return <div>
<div >
{children}
<input type="file" id="upload" onChange={this.onUpload} />
</div>
<span>{this.state.value}</span>
</div>;
}
}