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
Related
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}`}
I am working with ReactJs and Fetch API from Swagger UI. I code for Room management, this is my code for that
In actions.js file
// Room type action
export const actFetchRoomTypesRequest = () => {
return (dispatch) => {
return callApi('admin/host-room-types', 'GET', null).then((res) => {
dispatch(actFetchRoomTypes(res.data));
});
};
};
export const actFetchRoomTypes = (roomTypes) => {
return {
type: Types.FETCH_ROOMTYPES,
roomTypes,
};
};
export const actDeleteRoomTypeRequest = (id) => {
return (dispatch) => {
return callApi(`admin/host-room-types/${id}`, 'DELETE', null).then((res) => {
dispatch(actFetchRoomTypesRequest());
});
};
};
export const actAddRoomTypeRequest = (roomType) => {
return (dispatch) => {
return callApi(`admin/host-room-types`, 'POST', roomType).then((res) => {
return callApi(`admin/host-room-types`).then((ress) => {
dispatch(actFetchRoomTypes(ress.data));
});
});
};
};
export const actAddRoomType = (roomType) => {
return {
type: Types.ADD_ROOMTYPE,
roomType,
};
};
export const actGetRoomTypeRequest = (id) => {
return (dispatch) => {
return callApi(`admin/host-room-types/${id}`, 'GET', null).then((res) => {
dispatch(actGetRoomType(res.data));
});
};
};
export const actGetRoomType = (roomType) => {
return {
type: Types.EDIT_ROOMTYPE,
roomType,
};
};
export const actUpdateRoomTypeRequest = (roomType) => {
return (dispatch) => {
return callApi(`admin/host-room-types/${roomType.id}`, 'PUT', roomType).then((res) => {
return callApi(`admin/host-room-types`).then((ress) => {
dispatch(actFetchRoomTypes(ress.data));
});
});
};
};
export const actUpdateRoomType = (roomType) => {
return {
type: Types.UPDATE_ROOMTYPE,
roomType,
};
};
In RoomTypeItem.js which the place I show all action for that
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class RoomTypeItem extends Component {
constructor(props) {
super(props);
console.log('constructor');
}
onDelete = (id) => {
console.log(id);
if (window.confirm('ban muon xoa?')) {
this.props.onDelete(id);
}
};
onSelectRoomType = (id) => {
console.log('onSelect' + id);
this.props.onSelect(id);
};
render() {
var { roomType, index } = this.props;
console.log('render' + roomType.id + this.props.checked);
return (
<tr>
<td>
<input
type="checkbox"
className="delid[]"
checked={this.props.checked}
onChange={() => this.onSelectRoomType(roomType.id)}
/>
</td>
<td>{index + 1}</td>
<td>{roomType.name}</td>
<td>{roomType.description}</td>
<td>
<Link to={`/roomType/${roomType.id}/edit`} className="btn btn-warning mr-10">
Update
</Link>
</td>
</tr>
);
}
}
export default RoomTypeItem;
And in RoomTypeActionPage have code like this
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { actAddRoomTypeRequest, actGetRoomTypeRequest, actUpdateRoomTypeRequest } from './../../actions/index';
import { connect } from 'react-redux';
class RoomTypeActionPage extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
txtName: '',
txtDescription: '',
};
}
componentDidMount() {
var { match } = this.props;
if (match) {
var id = match.params.id;
this.props.onEditRoomType(id);
}
}
componentWillReceiveProps(nextProps) {
if (nextProps && nextProps.itemEditing) {
var { itemEditing } = nextProps;
this.setState({
id: itemEditing.id,
txtName: itemEditing.name,
txtDescription: itemEditing.description,
});
}
}
onChange = (e) => {
var target = e.target;
var name = target.name;
var value = target.type === 'checkbox' ? target.checked : target.value;
this.setState({
[name]: value,
});
};
onSave = (e) => {
e.preventDefault();
var { id, txtName, txtDescription } = this.state;
var { history } = this.props;
var roomType = {
id: id,
name: txtName,
description: txtDescription,
};
if (id) {
//update
this.props.onUpdateRoomType(roomType);
} else {
this.props.onAddRoomType(roomType);
}
history.goBack();
};
render() {
var { txtName, txtDescription } = this.state;
return (
<div className="col-xs-6 col-sm-6 col-md-6 col-lg-6">
<form onSubmit={this.onSave}>
<div className="form-group">
<label>Name:</label>
<input
type="text"
className="form-control"
name="txtName"
value={txtName}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label>Description:</label>
<input
type="text"
className="form-control"
name="txtDescription"
value={txtDescription}
onChange={this.onChange}
/>
</div>
<Link to="/roomType-list" className="btn btn-danger mr-10">
Cancel
</Link>
<button type="submit" className="btn btn-primary">
Save
</button>
</form>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
itemEditing: state.itemEditing,
};
};
const mapDispatchToProps = (dispatch, props) => {
return {
onAddRoomType: (roomType) => {
dispatch(actAddRoomTypeRequest(roomType));
},
onEditRoomType: (id) => {
dispatch(actGetRoomTypeRequest(id));
},
onUpdateRoomType: (roomType) => {
dispatch(actUpdateRoomTypeRequest(roomType));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(RoomTypeActionPage);
And this is my Reducer
import * as Types from './../constants/ActionTypes';
var initialState = {
loading: false,
data: [],
};
var findIndex = (roomTypes, id) => {
var result = -1;
roomTypes.data.forEach((roomType, index) => {
if (roomType.id === id) {
result = index;
}
});
return result;
};
const roomTypes = (state = initialState, action) => {
var index = -1;
var { id, roomType } = action;
switch (action.type) {
case Types.FETCH_ROOMTYPES:
state = action.roomTypes;
return { ...state };
case Types.DELETE_ROOMTYPE:
index = findIndex(state, id);
state.splice(index, 1);
return { ...state };
case Types.ADD_ROOMTYPE:
state.data.push(action.roomType);
return { ...state };
case Types.UPDATE_ROOMTYPE:
index = findIndex(state, roomType.id);
state[index] = roomType;
return { ...state };
default:
return { ...state };
}
};
export default roomTypes;
I do not know when I run my code it shows me an error like this
I already to check all but I can realize where I code wrong for that. PLease help me, I need you, Thank thank so much
This is my link for swagger UI : here
This is my apiCaller
import axios from 'axios';
import * as Config from './../constants/Config';
export default function callApi(endpoint, method = 'GET', body) {
return axios({
method: method,
url: `${Config.API_URL}/${endpoint}`,
data: body,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization:
'Bearer ' +
'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTU5Njg4MTYxMSwiZXhwIjoxNjEyOTQzNjEwfQ.eXkXWuuIMk94uOtVZQSNysbfJyIQuP5dIFqS06Kx-KsYVCkVEAbU01IhwJpkR4YtgXt8idkN4MM0cT76Xre7Sg',
},
}).catch((err) => {
console.log(err);
});
}
I am trying to get my photo blog/phlog manager component functional. However the console says the there an undefined object through props.
import React, { Component } from 'react';
import axios from 'axios';
import DropzoneComponent from 'react-dropzone-component';
import "../../../node_modules/react-dropzone-component/styles/filepicker.css";
import "../../../node_modules/dropzone/dist/min/dropzone.min.css";
class PhlogEditor extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
phlog_status: '',
phlog_image: '',
editMode: false,
position: '',
apiUrl: 'http://127.0.0.1:8000/phlogapi/phlog/',
apiAction: 'post'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.componentConfig = this.componentConfig.bind(this);
this.djsConfig = this.djsConfig.bind(this);
this.handlePhlogImageDrop = this.handlePhlogImageDrop.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.phlogImageRef = React.createRef();
}
deleteImage(event) {
event.preventDefault();
axios
.delete(
`http://127.0.0.1:8000/phlogapi/phlog/${this.props.id}/delete`,
{ withCredentials: true }
)
.then(response => {
this.props.handlePhlogImageDelete();
})
.catch(error => {
console.log('deleteImage failed', error)
});
}
The error is occuring at Object.keys(this.props.phlogToEdit).length>0
componentDidUpdate() {
if (Object.keys(this.props.phlogToEdit).length > 0) {
// debugger;
const {
id,
phlog_image,
phlog_status,
position
} = this.props.phlogToEdit;
this.props.clearPhlogsToEdit();
this.setState({
id: id,
phlog_image: phlog_image || '',
phlog_status: phlog_status || '',
position: position || '',
editMode: true,
apiUrl: `http://127.0.0.1:8000/phlogapi/phlog/${this.props.id}/update`,
apiAction: 'patch'
});
}
}
handlePhlogImageDrop() {
return {
addedfile: file => this.setState({ phlog_image_url: file })
};
}
componentConfig() {
return {
iconFiletypes: [".jpg", ".png"],
showFiletypeIcon: true,
postUrl: "https://httpbin.org/post"
};
}
djsConfig() {
return {
addRemoveLinks: true,
maxFiles: 3
};
}
buildForm() {
let formData = new FormData();
formData.append('phlog[phlog_status]', this.state.phlog_status);
if (this.state.phlog_image) {
formData.append(
'phlog[phlog_image]',
this.state.phlog_image
);
}
return formData;
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
handleSubmit(event) {
axios({
method: this.state.apiAction,
url: this.state.apiUrl,
data: this.buildForm(),
withCredentials: true
})
.then(response => {
if (this.state.phlog_image) {
this.phlogImageRef.current.dropzone.removeAllFiles();
}
this.setState({
phlog_status: '',
phlog_image: ''
});
if (this.props.editMode) {
this.props.handleFormSubmission(response.data);
} else {
this.props.handleSuccessfulFormSubmission(response.data);
}
})
.catch(error => {
console.log('handleSubmit for phlog error', error);
});
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit} className='phlog-editor-wrapper'>
<div className='one-column'>
<div className='image-uploaders'>
{this.props.editMode && this.props.phlog_image_url ? (
<div className='phlog-manager'>
<img src={this.props.phlog.phlog_image_url} />
<div className='remove-image-link'>
<a onClick={() => this.deleteImage('phlog_image')}>
Remove Photos
</a>
</div>
</div>
) : (
<DropzoneComponent
ref={this.phlogImageRef}
config={this.componentConfig()}
djsConfig={this.djsConfig()}
eventHandlers={this.handlePhlogImageDrop()}
>
<div className='phlog-msg'>Phlog Photo</div>
</DropzoneComponent>
)}
</div>
<button className='btn' type='submit'>Save</button>
</div>
</form>
);
}
}
export default PhlogEditor;
I do not understand how the object is empty when the props are coming from the parent component
phlog-manager.js:
import React, { Component } from "react";
import axios from "axios";
import PhlogEditor from '../phlog/phlog-editor';
export default class PhlogManager extends Component {
constructor() {
super();
Here I define phlogToEdit as an object to pass as props to phlogEditor child component
this.state = {
phlogItems: [],
phlogToEdit: {}
};
this.handleNewPhlogSubmission = this.handleNewPhlogSubmission.bind(this);
this.handleEditPhlogSubmission = this.handleEditPhlogSubmission.bind(this);
this.handlePhlogSubmissionError = this.handlePhlogSubmissionError.bind(this);
this.handleDeleteClick = this.handleDeleteClick.bind(this);
this.handleEditClick = this.handleEditClick.bind(this);
this.clearPhlogToEdit = this.clearPhlogToEdit.bind(this);
}
clearPhlogToEdit() {
this.setState({
phlogToEdit: {}
});
}
handleEditClick(phlogItem) {
this.setState({
phlogToEdit: phlogItem
});
}
handleDeleteClick(id) {
axios
.delete(
`http://127.0.0.1:8000/phlogapi/phlog/${id}`,
{ withCredentials: true }
)
.then(response => {
this.setState({
phlogItems: this.state.phlogItems.filter(item => {
return item.id !== id;
})
});
return response.data;
})
.catch(error => {
console.log('handleDeleteClick error', error);
});
}
handleEditPhlogSubmission() {
this.getPhlogItems();
}
handleNewPhlogSubmission(phlogItem) {
this.setState({
phlogItems: [phlogItem].concat(this.state.phlogItems)
});
}
handlePhlogSubmissionError(error) {
console.log('handlePhlogSubmissionError', error);
}
getPhlogItems() {
axios
.get('http://127.0.0.1:8000/phlogapi/phlog',
{
withCredentials: true
}
)
.then(response => {
this.setState({
phlogItems: [...response.data]
});
})
.catch(error => {
console.log('getPhlogItems error', error);
});
}
componentDidMount() {
this.getPhlogItems();
}
render() {
return (
<div className='phlog-manager'>
<div className='centered-column'>
This is where the object, phlogToEdit is being passed as props to child component mentioned
<PhlogEditor
handleNewPhlogSubmission={this.handleNewPhlogSubmission}
handleEditPhlogSubmission={this.handleEditPhlogSubmission}
handlePhlogSubmissionError={this.handleEditPhlogSubmission}
clearPhlogToEdit={this.clearPhlogToEdit}
phlogToEdit={this.phlogToEdit}
/>
</div>
</div>
);
}
}
#Jaycee444 solved the problem it was the parent component!
phlogToEdit={this.state.phlogToEdit}
I have a ReactJS webapp where I use webscokets to retrieve ticker prices from an API. When my webapp is standalone it works perfectly you can see in the image below.
But when I try to merge it in to my main react web app which has i18n localization no matter what I do I get this result. It seems it get stuck in the array or something. Maybe anyone has a clue how could I tackle this problem?
import React from 'react'
import axios from 'axios'
import { connect } from 'react-redux'
import Loading from '../elements/Loading'
import Ticker from './Ticker'
import TradeHistory from './TradeHistory'
import OrderBook from './OrderBook'
class Trade extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoaded: false
};
this.tradesCount = 20;
this.streams = ['#ticker','#depth20','#trade'];
}
_connectSocketStreams(streams) {
streams = streams.join('/');
let connection = btoa(streams);
this[connection] = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${streams}`);
this[connection].onmessage = evt => {
let eventData = JSON.parse(evt.data);
if(eventData.stream.endsWith('#ticker')){
eventData.data.lastc = this.state.ticker ? this.state.ticker.c : 0
this.props.dispatch({
type: 'SET_TICKER',
data: eventData.data
})
this.setState({
loadedTicker: true
})
}
if(eventData.stream.endsWith('#trade')){
if(this.props.trades && Object.keys(this.props.trades).length > 0){
let trades = this.props.trades;
trades.push(eventData.data);
trades = trades.slice(-1*this.tradesCount);
this.props.dispatch({
type: 'SET_TRADES',
data: trades
})
this.setState({
loadedTrades: true
})
}
}
if(eventData.stream.endsWith('#depth20')){
this.props.dispatch({
type: 'SET_DEPTH',
data: eventData.data
})
this.setState({
loadedDepth: true
})
}
this.setState({
isLoaded: true
})
};
this[connection].onerror = evt => {
console.error(evt);
}
}
_disconnectSocketStreams(streams){
streams = streams.join('/');
let connection = btoa(streams);
if (this[connection].readyState === WebSocket.OPEN) {
this[connection].close();
}
}
componentDidMount() {
let symbol = this.props.match.params.symbol.toLowerCase();
this._connectSocketStreams(this.streams.map(i => `${symbol}${i}`));
axios({
method: 'get',
url: `https://cors-anywhere.herokuapp.com/https://www.binance.com/api/v1/aggTrades?limit=${this.tradesCount}&symbol=${this.props.match.params.symbol}`
})
.then(response => {
this.props.dispatch({
type: 'SET_TRADES',
data: response.data
})
this.setState({
isLoaded: true,
loadedTrades: true
})
})
.catch(error => {
this.setState({
isLoaded: false,
error: error
})
});
}
componentWillUnmount() {
let symbol = this.props.match.params.symbol.toLowerCase();
this._disconnectSocketStreams(this.streams.map(i => `${symbol}${i}`))
}
render() {
const { error, isLoaded, loadedDepth, loadedTicker, loadedTrades } = this.state;
if (error) {
return <div className="alert alert-danger">{error.message}</div>;
}
if (!isLoaded) {
return <Loading />;
}
return (
<React.Fragment>
<div className="row">
<div className="col-12">{loadedTicker ? <Ticker {...this.props.ticker} /> : <Loading />}</div>
</div>
<div className="row">
<div className="col-12 col-sm-6">{loadedTrades ? <TradeHistory trades={this.props.trades}/> : <Loading />}</div>
</div>
</React.Fragment>
)
}
}
export default connect(
state => state
)(Trade)
I have a problem when want to show
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
userId: state.auth.userId,
tokenId: state.auth.token
}
}
this in my function
register = (event) => {
event.preventDefault()
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup );
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
}
I see token and userId after the second click. But I can't see after the first click. What I need more to show immediately?
This is my auth.js reducers
import * as actionTypes from '../actions/actionsTypes';
import { updateObject } from '../utility';
const initialState = {
token: null,
userId: null,
error: null,
loading: false
};
const authStart = ( state, action ) => {
return updateObject( state, { error: null, loading: true } );
};
const authSuccess = (state, action) => {
return updateObject( state, {
token: action.idToken,
userId: action.userId,
error: null,
loading: false
} );
};
const authFail = (state, action) => {
return updateObject( state, {
error: action.error,
loading: false
});
}
const reducer = ( state = initialState, action ) => {
switch ( action.type ) {
case actionTypes.AUTH_START: return authStart(state, action);
case actionTypes.AUTH_SUCCESS: return authSuccess(state, action);
case actionTypes.AUTH_FAIL: return authFail(state, action);
default:
return state;
}
};
export default reducer;
But, after the first click, I got token in my render function.
{this.props.tokenId}
Could you please help me? I think I need to use async/await. But I am not sure.
Here you go Header.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import * as actions from '../../store/actions/index'
import PropTypes from 'prop-types'
import './header.css'
class Header extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
isSignup: true,
token: false
}
this.handleChange = this.handleChange.bind(this);
}
handleChange (evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
switchAuthModeHandler = (event) => {
event.preventDefault()
this.setState(prevState => {
return {
isSignup: !prevState.isSignup
}
})
}
register = (event) => {
event.preventDefault()
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup );
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
}
render() {
let regBtn = ''
if (this.state.isSignup) {
regBtn = 'Register'
}
else {
regBtn = 'Login'
}
let login = null
if(!this.props.tokenId) {
login = (
<div className="login">
<form onSubmit={this.register}>
<input type="email" placeholder="email" name="email" onChange={this.handleChange} />
<input type="password" placeholder="password" name="password" onChange={this.handleChange} />
<button>{regBtn}</button>
</form>
<div onClick={this.switchAuthModeHandler} className="switch">Switch to {this.state.isSignup ? 'Login' : 'Register'}</div>
</div>
)
}
else {
login = (
<div>
<p>Hello: {this.props.userId}</p>
<button>Logout</button>
</div>
)
}
if(this.props.loading) {
login = <div>Loading...</div>
}
return (
<div>
<div className="header-inner">
{this.props.tokenId}
{login}
<img src="http://civcic.com/assets/images/header-bg.jpg" alt="img" />
<div className="header-content">
<h2>React.JS DEVELOPER</h2>
<a className="knowmore-btn" href="https://www.upwork.com/freelancers/~01f507600be26cc2a3" rel="noopener noreferrer" target="_blank">Upwork profile</a><br />
<a className="knowmore-btn" href="https://www.linkedin.com/in/boris-civcic-37244378/" rel="noopener noreferrer" target="_blank">Linkedin</a><br />
<a className="knowmore-btn" href="https://github.com/fixman93" rel="noopener noreferrer" target="_blank">GitHub</a>
</div>
</div>
</div>
)
}
}
Header.defaultProps = {
tokenId: ''
}
Header.propTypes = {
tokenId: PropTypes.string
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
userId: state.auth.userId,
tokenId: state.auth.token
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: ( email, password, isSignup) => dispatch( actions.auth(email, password, isSignup))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header)
import axios from 'axios';
import * as actionTypes from './actionsTypes';
export const authStart = () => {
return {
type: actionTypes.AUTH_START
}
}
export const authSuccess = (token, userId) => {
return {
type: actionTypes.AUTH_SUCCESS,
idToken: token,
userId: userId
}
}
export const authFail = (error) => {
return {
type: actionTypes.AUTH_FAIL,
error: error
};
};
export const auth = (email, password, isSignup) => {
return dispatch => {
dispatch(authStart());
const authData = {
email: email,
password: password,
fullName: 'Boris Civcic',
role: 'admin',
returnSecureToken: true
};
let url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyC5nW8-XOJADEvU7Mi7sgmhUNhHfRxXNQI';
if (!isSignup) {
url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=AIzaSyC5nW8-XOJADEvU7Mi7sgmhUNhHfRxXNQI';
}
axios.post(url, authData)
.then(response => {
console.log(response);
dispatch(authSuccess(response.data.idToken, response.data.localId));
// dispatch(checkAuthTime(response.data.expiresIn));
})
.catch(err => {
dispatch(authFail(err.response.data.error));
})
};
};
this is auth.js action
and this is utility
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
In your register handler, onAuth is an asynchronous action but you've populated localStorage immediately. You should wait onAuth returns and then set your localStorage items.
first return a promise from your thunk ( simply by adding return before axios ):
...
return axios.post(url, authData)
.then(response => {
console.log(response);
dispatch(authSuccess(response.data.idToken, response.data.localId));
// dispatch(checkAuthTime(response.data.expiresIn));
})
.catch(err => {
dispatch(authFail(err.response.data.error));
})
...
Then set your localStorage items like this:
register = (event) => {
event.preventDefault();
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup )
.then(() => {
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
});
}