I am having a bit of trouble with my project with spotify, every time I try to search for an artist I get this message: illegal redirect_uri. I also get a list of issues that I am boggled with. Would anyone have any advice? Here is my code.
src\Components\SearchBar\SearchBar.js
Line 25:5: Duplicate name ‘handleTermChange’ no-dupe-class-members
src\util\Spotify.js
Line 1:7: ‘clientId’ is assigned a value but never used no-unused-vars
Line 2:7: ‘redirectUri’ is assigned a value but never used no-unused-vars
Line 25:31: Unexpected template string expression no-template-curly-in-string
Line 31:15: ‘accessToken’ is assigned a value but never used no-unused-vars
Line 32:22: Unexpected template string expression no-template-curly-in-string
Line 34:27: Unexpected template string expression no-template-curly-in-string
Line 57:15: ‘accessToken’ is assigned a value but never used no-unused-vars
Line 58:41: Unexpected template string expression no-template-curly-in-string
Line 64:13: ‘userId’ is assigned a value but never used no-unused-vars
Line 65:27: Unexpected template string expression no-template-curly-in-string
Line 72:23: ‘playlistId’ is assigned a value but never used no-unused-vars
import React from 'react';
import './SearchBar.css';
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state ={
term: ''
}
this.search = this.search.bind(this);
this.handleTermChange = this.handleTermChange.bind(this);
}
search() {
this.props.onSearch(this.state.term);
}
handleTermChange(event) {
this.setState({term: event.target.value});
}
handleTermChange(event) {
this.setState({term: event.target.value});
}
render() {
return (
<div className="SearchBar">
<input onChange={this.handleTermChange} placeholder="Enter A Song, Album, or Artist" />
<button className="SearchButton" onClick={this.search} >SEARCH</button>
</div>
)
}
}
export default SearchBar;
const clientId = '5e56a43c5001426189eda044053e2d30';
const redirectUri = 'http://localhost:3000/'
let accessToken;
const Spotify = {
getAccessToken() {
if (accessToken) {
return accessToken;
}
// check for access token match
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
if(accessTokenMatch && expiresInMatch) {
accessToken = accessTokenMatch[1];
const expiresIn = Number(expiresInMatch[1]);
//This clears the parameters, allowing us to grab a new access token when it expires.
window.setTimeout(() => accessToken = '', expiresIn * 1000);
window.history.pushState('Access Token' , null, '/');
return accessToken;
}else{
const accessUrl = 'https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}'
window.location = accessUrl;
}
},
search(term) {
const accessToken = Spotify.getAccessToken();
return fetch('https://api.spotify.com/v1/search?type=track&q=${term}',
{headers: {
Authorization:'Bearer ${accessToken}'
}
}).then(response => {
return response.json();
}).then(jsonResponse => {
if(!jsonResponse.tracks) {
return [];
}
return jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artists[0].name,
album: track.album.name,
uri: track.uri
}));
}) ;
},
savePlayList(name, trackUris) {
if(!name || !trackUris.length) {
return;
}
const accessToken = Spotify.getAccessToken();
const headers = {Authorization: 'Bearer ${accessToken}'};
let userId;
return fetch('https://api.spotify.com/v1/me', {headers:headers}
).then(response => response.json()
).then(jsonResponse => {
userId = jsonResponse.Id;
return fetch ('https://api.spotify.com/v1/users/${user_id}/playlists',
{
headers:headers,
method:'POST',
body: JSON.stringify({name: name})
}).then(response => response.json()
).then(jsonResponse => {
const playlistId = jsonResponse.id;
return fetch('https://api.spotify.com/v1/users/${userId}/playlists',
{
headers: headers,
method: 'POST',
body: JSON.stringify({Uris: trackUris})
});
})
})
}
}
export default Spotify;
import React from 'react';
import './App.css';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
import Spotify from '../../util/Spotify';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
playlistName: 'My Playlist',
playlistTracks: []
};
this.addTrack = this.addTrack.bind(this);
this.removeTrack = this.removeTrack.bind(this);
this.updatePlaylistName = this.updatePlaylistName.bind(this);
this.savePlaylist = this.savePlaylist.bind(this);
this.search = this.search.bind(this);
}
addTrack(track) {
let tracks = this.state.playlistTracks;
if(tracks.find(savedTrack => savedTrack.id === track.id)){
return;
}
tracks.push(track);
this.setState({playlistTracks: tracks})
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);
this.setState({playlistTracks: tracks});
}
updatePlaylistName(name) {
this.setState({playlistName: name});
}
savePlaylist() {
const trackUris = this.state.playlistTracks.map(track => track.uri);
Spotify.savePlaylist(this.state.playlistName, trackUris).then(()=> {
this.setState({
playlistName: 'New Playlist',
playlistTracks: []
})
})
}
search(term) {
Spotify.search(term).then(searchResults => {
this.setState({searchResults: searchResults})
})
}
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar onSearch={this.search} />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults}
onAdd={this.addTrack} />
<Playlist playstName={this.state.playlistName}
playlistTracks={this.state.playlistTracks}
onRemove = {this.removeTrack}
onNameChange ={this.updatePlaylistName}
onSave = {this.savePlaylist} />
</div>
</div>
</div>
)
}
}
export default App;
Your Template literals is wrongly declare, they all need to start and end with a backtick (`)
It should be:
const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`
With (`) not (')
And so on with your other Template literals, it should all start and end with backticks (`);
return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`,
Authorization:`Bearer ${accessToken}`
And so on...
Also, remove one of the handleTermChange functions.
First, you have 2 handleTermChange. Remove one of theme.
Second, you use Template literals wrong way. So clientId will not to use. Just update like this with {``}
const accessUrl = {`https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`}
Related
Well i am trying to reduce the line of code at once refactoring the code
import React, { Component } from 'react';
import { Loader } from '../../components';
import './ProductListing.scss';
import { ProductCard } from '../../components';
import { productQuery } from '../../utls/queries';
export class ProductListing extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
products: [],
categoryId: '',
};
}
componentDidMount() {
const currentUrl = window.location.pathname;
const id = currentUrl.replace('/', '');
this.setState({ categoryId: id });
const newQuer = { ...productQuery };
const query = `
query{
categories {
name
products {
id,
name,
brand,
inStock,
gallery,
category,
prices {
amount,
currency {
label,
symbol
}
}
}
}
}
`;
console.log(query === productQuery);
console.log(productQuery);
fetch('http://localhost:4000', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
query,
}),
})
.then((response) => {
return response.json();
})
.then((data) => {
this.setState({
products: data.data,
loading: false,
});
});
}
render() {
if (this.state.loading === true) {
return <Loader />;
} else {
return (
<div>
<h2 className='page__listing__title'>
{this.state.categoryId[0].toUpperCase() +
this.state.categoryId.substring(1)}
</h2>
<div className='productlisting__page'>
{this.state.products.categories.map((item, index) => (
<div key={index}>
{item.name === this.state.categoryId ? (
<div className='product__listing__card'>
{item.products.map((product, i) => (
<ProductCard product={product} key={i} />
))}
</div>
) : (
''
)}
</div>
))}
</div>
</div>
);
}
}
}
export default ProductListing;
In the process of reducing code i see that the query is taking a lot of places so i decided to write it at separate place now i am importing it as productQuery when i console.log(productQuery===query) it says true but the place where i am using the query to fetch data i use productQuery it just give bad error i cant understand ...
if some one have much better idea i really like if you can suggest me much better ways by which i can reduce the lines of code
I think what's happening is you're accidentally destructuring the query you import, when you say
const newQuery = {...productQuery}
productQuery is simply a string (as proven by your console log that stays productQuery === query).
newQuery is an object that destructures the string, and trying to use that would likely result in a failure.
I've created a registration api that works fine using postman, I added some values there plus image an everything stores correctly, data an image, Later I started my React form and only text works, image is not currently sent to the api I guess.
Doing console.log() api in Node:
console.log(req.files);
with React Form: []
with Postman: an array of object that perfectly works
req.files output using postman
{
fieldname: 'images',
originalname: 'Screen Shot 2021-02-22 at 17.18.41.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: '091f77f82fb805b1ede9f23205cc578e',
path: 'uploads/091f77f82fb805b1ede9f23205cc578e',
size: 37052
}
Here's my react classes:
httpService.js
import axios from "axios";
import { toast } from "react-toastify";
axios.interceptors.response.use(null, (error) => {
const expectedError =
error.response &&
error.response.status >= 400 &&
error.response.status < 500;
if (!expectedError) {
console.log("Loggind the error: ", error);
toast("An unexpected error ocurred.");
}
return Promise.reject(error);
});
export default {
post: axios.post
};
itemService.js
export function saveItem(item) {
const headers = {
'Content-Type': 'multipart/form-data',
};
return http.post(MY_ENDPOINT, item, headers);
}
itemForm.js
import React from "react";
import Joi from "joi-browser";
import Form from "./common/form";
import { saveItem } from "../services/itemService";
class ItemForm extends Form {
state = {
data: { title: "", description: "", category: "", images: "" },
categories: [],
errors: {},
};
schema = {
_id: Joi.string(),
title: Joi.string().required().label("Title"),
description: Joi.string().required().label("Description"),
category: Joi.string().required().label("Category"),
images: Joi.required().label("Image"),
};
doSubmit = async () => {
console.log('form data> ', this.state.data); // This shows the correct object.
let formData = new FormData();
formData.append('title', this.state.data.title);
formData.append('description', this.state.data.description);
formData.append('category', this.state.data.category);
formData.append('images', this.state.data.images);
try {
await saveItem(formData);
} catch (ex) {
}
};
render() {
return (
<div>
<h1>New item</h1>
<form onSubmit={this.handleSubmit}>
{this.renderInput("title", "Title")}
{this.renderInput("description", "Description")}
{this.renderSelect(
"category",
"title",
"Category",
this.state.categories
)}
{this.renderInputFile("images", "images", "file", false)}
{this.renderButton("Register")}
</form>
</div>
);
}
}
export default ItemForm;
form.jsx (The extended class)
import React, { Component } from "react";
import Joi from "joi-browser";
import Input from "./input";
import Select from "./select";
class Form extends Component {
state = {
data: {},
errors: {},
};
validate = () => {
const options = { abortEarly: false };
const { error } = Joi.validate(this.state.data, this.schema, options);
if (!error) return null;
const errors = {};
for (let item of error.details) errors[item.path[0]] = item.message;
return errors;
};
validateProperty = ({ name, value }) => {
const obj = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(obj, schema);
return error ? error.details[0].message : null;
};
handleSubmit = (e) => {
e.preventDefault();
const errors = this.validate();
this.setState({ errors: errors || {} });
if (errors) return;
this.doSubmit();
};
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data, errors });
};
handleInputFileChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data, errors });
};
renderButton(label) {
return (
<button className="btn btn-primary" disabled={this.validate()}>
{label}
</button>
);
}
renderInput(name, label, type = "text", multiple = false) {
const { data, errors } = this.state;
return (
<Input
type={type}
name={name}
value={data[name]}
label={label}
onChange={this.handleChange}
error={errors[name]}
multiple={multiple}
/>
);
}
renderSelect(name, contentField, label, options) {
const { data, errors } = this.state;
return (
<Select
name={name}
value={data[name]}
label={label}
contentField={contentField}
options={options}
onChange={this.handleChange}
error={errors[name]}
/>
);
}
renderInputFile(name, label, type = "text", multiple = false) {
const { data, errors } = this.state;
return (
<Input
type={type}
name={name}
value={data[name]}
label={label}
onChange={this.handleInputFileChange}
error={errors[name]}
multiple={multiple}
accept="image/*"
/>
);
}
}
export default Form;
This is a React project that interacts with Spotify API. Everything seems to be working until my search function in Spotify.js returns an array with the track information, which is chained to setState in App.js but I get an error: ×
TypeError: Cannot read property 'then' of undefined
Sorry if too much or not enough info... Completely new to coding and appreciate anyone who can help me understand what's going wrong. Thank you!
App.js
import React from 'react';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
import Spotify from '../../Util/Spotify';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
playlistName: 'playlist1',
playlistTracks: []
};
this.addTrack = this.addTrack.bind(this);
this.removeTrack = this.removeTrack.bind(this);
this.updatePlaylistName = this.updatePlaylistName.bind(this);
this.savePlaylist = this.savePlaylist.bind(this);
this.search = this.search.bind(this);
}
addTrack(track) {
let tracks = this.state.playlistTracks;
if (tracks.find(savedTrack => savedTrack.id === track.id)) {
return;
} else {
tracks.push(track);
}
this.setState({playlistTracks: tracks});
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);
this.setState({ playlistTracks: tracks });
}
updatePlaylistName(name) {
this.setState({ playlistName: name })
}
savePlaylist() {
const trackUris = this.state.playlistTracks.map(track => track.uri);
Spotify.savePlaylist(this.state.playlistName, trackUris).then(() => {
this.setState({
playlistName: 'New Playlist',
playlistTracks: []
})
})
}
search(term) {
Spotify.search(term).then(searchResults => {
this.setState({ searchResults: searchResults })
});
}
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar onSearch={this.search} />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults}
onAdd={this.addTrack} />
<Playlist playlistName={this.state.playlistName}
playlistTracks={this.state.playlistTracks}
onRemove={this.removeTrack}
onNameChange={this.updatePlaylistName}
onSave={this.savePlaylist} />
</div>
</div>
</div>
)
}
}
export default App;
App.js passes search to SearchBar.js
import React from 'react';
import './SearchBar.css';
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
term: ''
}
this.search = this.search.bind(this);
this.handleTermChange = this.handleTermChange.bind(this);
}
search() {
this.props.onSearch(this.state.term);
}
handleTermChange(event) {
this.setState({
term: event.target.value
})
}
render() {
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist"
onChange={this.handleTermChange} />
<button onClick={this.search} className="SearchButton">SEARCH</button>
</div>
)
}
}
export default SearchBar;
SearchBar.js passes term back to App.js, calls search in Spotify.js:
let accessToken;
const clientID = 'clientID';
const redirectURI = "http://localhost:3000/";
const Spotify = {
search(term) {
const accessToken = Spotify.getAccessToken();
fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`,
{ headers: { Authorization: `Bearer ${accessToken}` }
}).then(response => {
return response.json();
}).then(jsonResponse => {
if (!jsonResponse.tracks) {
return [];
}
return jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artists[0].name,
album: track.album.name,
uri: track.uri
}));
});
},
savePlaylist(name, trackUris) {
if (!name || !trackUris.length) {
return;
};
let accessToken = Spotify.getAccessToken();
let headers = {Authorization: `Bearer ${accessToken}`};
let userID;
return fetch('https://api.spotify.com/v1/me', {headers: headers}
).then(response => {
response.json();
}).then(jsonResponse => {
userID = jsonResponse.id;
return fetch(`https://api.spotify.com/v1/user/${userID}/playlists`,
{
headers: headers,
method: 'POST',
body: JSON.stringify({name: name})
}).then(response => response.json()
).then(jsonResponse => {
const playlistID = jsonResponse.id;
return fetch(`/v1/users/${userID}/playlists/${playlistID}/tracks`,
{
headers: headers,
method: 'POST',
body: JSON.stringify({uris: trackUris})
})
})
})
},
getAccessToken() {
if (accessToken) {
return accessToken;
}
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);
if (accessTokenMatch && expiresInMatch) {
accessToken = accessTokenMatch[1];
const expiresIn = Number(expiresInMatch[1]);
window.setTimeout(() => accessToken = '', expiresIn * 1000);
window.history.pushState('Access Token', null, '/');
return accessToken;
} else {
const accessURL = `https://accounts.spotify.com/authorize?client_id=${clientID}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectURI}`;
window.location = accessURL;
}
}
};
export default Spotify;
Spotify.search returns an array, but App.search says: TypeError: Cannot read property 'then' of undefined
I'm trying to render the data from my database get this instead Failed to compile.
./src/components/list-pets.component.js
Line 38:5: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.enter code here
Here is my code from the trouble component
import React, { Component } from 'react';
import axios from 'axios';
export default class ListPets extends Component {
constructor(props) {
super(props);
this.state = {
pets: []
};
}
componentDidMount = () => {
this.getPets();
};
getPets = () => {
axios.get('http://localhost:5000/pets')
.then((response) => {
const data = response.data;
this.setState({ pets: data });
console.log('Data has been received!');
})
.catch((err) => {
console.log(err);
});
}
displayPet = (pets) => {
if (!pets.length) return null;
return pets.map((pet, index) => {
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
});
};
render() {
console.log('State: ', this.state);
return (
<div className='adopt'>
{this.displayPet(this.state.pets)}
</div>
)
}
}
You need to return a value at each pets.map iteration, currently you’re returning undefined.
return pets.map((pet, index) => {
return (
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
)
});
You have to wait until fetching data is completed.
You should have to define the loading bar while fetching.
class App extends Component {
constructor() {
super();
this.state = {
pageData: {},
loading: true
}
this.getData();
}
async getData(){
const res = await fetch('/pageData.json');
const data = await res.json();
return this.setState({
pageData: data,
loading: false
});
}
componentDidMount() {
this.getData();
}
render() {
const { loading, pageData } = this.state;
if (loading){
return <LoadingBar />
}
return (
<div className="App">
<Navbar />
</div>
);
}
}
I have a problem with a static method in React using ESLint with airbnb config. I have a service like this that is both used for creating a user in my system, and getting all the field values for the create user form. The service looks like this:
import axios from 'axios';
import ServiceException from './ServiceException';
class CreateUserServiceException extends ServiceException {}
class CreateUserService {
constructor(config) {
this.apiUrl = config.API_URL;
this.userDomain = config.USER_DOMAIN;
}
static getFormFields() {
return [
{
id: 'username',
type: 'text',
title: 'E-postadress',
placeholder: 'Användarnamn',
mandatory: true,
extra: '',
},
{
id: 'password',
type: 'password',
title: 'Lösenord',
placeholder: 'Lösenord',
mandatory: true,
extra: '',
},
];
}
async createUser(data) {
try {
await axios.post(`${this.apiUrl}/users/${this.userDomain}`, data, { withCredentials: true });
} catch ({ response }) {
throw new CreateUserServiceException(
response.status, 'Failed to create user', response.data,
);
}
}
}
export default CreateUserService;
I also have a jsx controller to create my form. This controller gets the service via it's properties. The controller looks like this:
import React from 'react';
import './index.css';
class CreateUserController extends React.Component {
constructor(props) {
super(props);
this.state = {
formFields: [],
userData: {},
};
this.onCreate = this.onCreate.bind(this);
this.onLoad = this.onLoad.bind(this);
}
async componentDidMount() {
await this.onLoad();
}
async onLoad() {
const { createUserService } = await this.props;
const { getFormFields } = createUserService;
const formFields = getFormFields || []; // ALWAYS RETURNS UNDEFINED
const userData = {};
console.log(formFields); // ALWAYS DISPLAYS []
formFields.forEach((field) => {
userData[field.id] = '';
});
this.setState({ formFields, userData });
}
async onCreate(e) {
e.preventDefault();
const { userData } = this.state;
console.log(userData);
}
render() {
const { userData, formFields } = this.state;
return (
<section className="create-user-controller">
<h1>Skapa ny användare</h1>
<form
className="centered-container"
action=""
noValidate
onSubmit={this.onCreate}
>
<table>
<tbody>
{formFields.map(field => (
<tr key={field.id}>
<td>{field.title}</td>
<td>
<input
value={userData[field.id]}
onChange={e => this.setState({
userData: { ...userData, [field.id]: e.target.value },
})}
className={`create-${field.id}`}
name={field.id}
placeholder={field.placeholder}
type={field.type}
/>
</td>
<td>{field.extra}</td>
</tr>
))}
<tr>
<td colSpan={3}>* obligatoriskt</td>
</tr>
</tbody>
</table>
<input type="submit" className="btn btn-green" value="Skapa användare" />
</form>
</section>
);
}
}
export default CreateUserController;
My problem is that const formFields = getFormFields || []; always becomes [] which means that getFormFields always returns undefined.
If I remove static from getFormFields() in my service and call it using const formFields = createUserService.getFormFields(); it works fine, but then ESLint complains about ESLint: Expected 'this' to be used by class method 'getFormFields'. (class-methods-use-this).
Does anyone have an idea how to solve this?
import CreateUserService from './CreateUserService';
...
async onLoad() {
...
const formFields = CreateUserService.getFormFields() || [];
...
}
Should do the trick !
Notice that the static function is called using the Class name. Tou will also have to import it correctly (i don't know your path…)