Problem requesting data based on URL parameters in React-Redux - node.js

On a MERN-stack application, I would like to request data from a particular user's schema based on the url parameters. For instance, http://localhost:3000/user/User1 would render data from User 1. I am using redux-react for state management. Here is what I currently have, and here is what's happening.
Userpage Component
import React, { Component } from "react";
import {getUser} from '../../actions/userActions';
import {connect} from 'react-redux'
class Userpage extends Component {
componentDidMount() {
getUser(this.props.match.params.username)
}
render() {
return (
<div>
<h1>Hello</h1>
<h3>{this.props.user.name}</h3>
<h3>{this.props.user.email}</h3>
</div>
);
}
}
const mapStatetoProps = state => {
return{user: state.user}
};
export default connect(mapStatetoProps, getUser) (Userpage)
userActions
import axios from 'axios';
import {GET_USER} from './types';
export const getUser = username => dispatch => {
axios
.get(`/api/users/${username}`)
.then(({res}) =>
dispatch({
type: GET_USER,
payload: username
})
)
};
userReducer
import { GET_USER } from "../actions/types";
const initialState = {
user: {},
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_USER:
return{
...state,
user: action.payload,
}
}
Then in the api/users route, I have the following:
router.get("/:username", (req, res) => {
User.findOne({username: req.params.username})
.then(user => res.json(user))
});
Beyond the user state returning as empty, here are some errors I am getting.
On the command line, I occasionally get a
Could not proxy request /api/users/function%20(action)%20%7B%20%20%20%20%20%20%20%20if%20(typeof%20action%20===%20'function')%20%7B%20%20%20%20%20%20%20%20%20%20return%20action(dispatch,%20getState,%20extraArgument);%20%20%20%20%20%20%20%20%7D%20%20%20%20%20%20%20%20return%20next(action);%20%20%20%20%20%20%7D from localhost:3000 to http://localhost:5000.
As well as a
mapDispatchToProps() in Connect(Userpage) must return a plain object. Instead received undefined.
Any help or guidance would be greatly appreciated.

Related

I'm fetching dynamic data from my nodejs to reactjs but I get an error saying "POST IS NOT DEFINED"

I have made entries in my mongodb database using node now I'm trying to fetch that data from backend to react front-end the 3rd party app used for cross-platform in node are cors and for react is axios(in script I have added "proxy":"http://localhost:5000"(5000 is my backend port)
Here is my code for NovelCard.js
import React, { Component } from 'react';
import { Container } from 'react-bootstrap';
import Card from 'react-bootstrap/Card';
import axios from 'axios';
const createSet = (post) => {
<Card style={{ width: '18rem' }}>
<Card.Img variant="top" src="holder.js/100px180" />
<Card.Body>
<Card.Title>{post.name}</Card.Title>
<Card.Subtitle className="mb-2 text-muted">{post.author}</Card.Subtitle>
<Card.Text>{post.synopsis}</Card.Text>
</Card.Body>
</Card>;
};
class Latest extends Component {
state = {
name: '',
author: '',
synopsis: '',
post: [],
};
componentDidMount = () => {
this.getData();
};
getData = () => {
axios
.get('http://localhost:5000/novels/')
.then((res) => {
const data = res.data;
this.setState({ post: data });
console.log('data recived');
})
.catch(() => {
alert('error');
});
};
render() {
console.log('state', this.state);
return <Container>{post.map(createSet)}</Container>;
}
}
export default Latest;
I'm getting error saying ***
src\components\home\Latest\NovelCard.js Line 45:24: 'post' is not
defined no-undef
Your post variable is available within you state. You need to do something like this within your render function.
render() {
console.log('state', this.state);
return <Container>{this.state.post.map(createSet)}</Container>;
}
Or you can do like this as well.
render() {
const { post } = this.state;
console.log('state', this.state);
return <Container>{post.map(createSet)}</Container>;
}

How to receive JSON Object from node and display it on client side in react?

I am sending a json object using res.json. On the client side I am trying to set the json object to a piece of state.
Ive tried to .json() the response but that still does not let me assign it.
This is the server side sending the JSON File
app.get('/api/getPlace', async (req, res) => {
const response = await client.search({
searchType: "Coffee",
location: "San Francisco, CA",
})
const foodPlace = response.jsonBody.businesses[9];
console.log(foodPlace);
res.json(foodPlace)
})
Below is the whole component file to render the json object
import React, { Component } from 'react';
import axios from 'axios';
class RandomPlace extends Component {
constructor(props) {
super(props);
this.state = {
response: {},
};
}
async componentDidMount() {
const res = axios.get('/api/getPlace');
this.setState({ response: res.data })
}
render() {
return (
<div>
{this.state.response}
</div>
);
}
}
export default RandomPlace;
The client call must be awaited:
async componentDidMount() {
const res = await axios.get('/api/getPlace');
this.setState({ response: res.data })
}
import React, { Component } from 'react';
import axios from 'axios';
class RandomPlace extends Component {
constructor(props) {
super(props);
this.state = {
response: {},
};
}
async componentDidMount() {
const res = await axios.get('/api/getPlace');
this.setState({ response: res.data })
}
render() {
return (
<div>
{this.state.response}
</div>
);
}
}
export default RandomPlace;
REST api calls are asynchronous, which means the code proceeds to the next statement without waiting for the api call to compelete. When await is adding before the call, the execution will pause till the call completes or timesout (if specified) before proceeding to the next line. async/await is a better alternative to promises.

ReactJS - redirect on the login page if session expired

I have followed some tutorial to build an authentication in React, Node and Redux. The basic functionality works, however, when I keep the application open and then get back to it (when the session expired), I get this error message:
Unhandled Rejection (TypeError): Cannot read property 'uploadURL' of undefined
Then I refresh the page and I get this error message:
TypeError: Cannot read property 'push' of undefined
Then, I refresh the page again and I am finally redirected on the homepage. The first 2 errors are a problem I am not sure how to get rid off them.
This is what my code looks like:
...
class Event extends Component {
constructor() {
super();
...
}
UNSAFE_componentWillMount() {
// I thought this if-block will redirect the user if the session is expired
if(!this.props.auth.isAuthenticated) {
console.log('unauthorized');
this.props.history.push('/');
}
this.uppy2 = new Uppy({ id: 'uppy2', autoProceed: true, debug: true })
.use(Tus, { endpoint: 'https://master.tus.io/files/' })
.on('complete', (result) => {
console.log(`Upload complete! We’ve uploaded these files: ${result.successful[0].uploadURL}`);
});
}
...
}
Event.propTypes = {
registerUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
export default connect(mapStateToProps,{ registerUser })(withRouter(Event))
Here's the Redux code (I am beginner with the MERN stack):
import axios from 'axios';
import { GET_ERRORS, SET_CURRENT_USER } from './types'; // we list here the actions we'll use
import setAuthToken from '../../setAuthToken';
import jwt_decode from 'jwt-decode';
export const registerUser = (user, history) => dispatch => {
axios.post('/api/users/register', user)
.then(res => history.push('/login'))
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err.response.data
});
});
}
export const loginUser = (user) => dispatch => {
axios.post('/api/users/login', user)
.then(res => {
//console.log(res.data);
const { token } = res.data;
localStorage.setItem('jwtToken', token);
setAuthToken(token);
const decoded = jwt_decode(token);
dispatch(setCurrentUser(decoded));
})
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err.response.data
});
});
}
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
}
}
export const logoutUser = (history) => dispatch => {
localStorage.removeItem('jwtToken');
setAuthToken(false);
dispatch(setCurrentUser({}));
history.push('/login');
}
How do I prevent the errors happening when the session is expired?
Thank you in advance!
ComponentWillMount won't be called if the page is loaded before the session expires. I suspect the first error is caused by some missing data because the request with the expired token failed. You would need to make sure the 401 or 403 error is handled and clear out the Redux state so the login page is shown when that happens.
I am not sure with this part !this.props.auth.isAuthenticated. Did you use mapDispatchToProps and connect for redux? You need to do this in your Event class to reach your reducer.
Also the thing that you can do is, before rendering your jsx code, declare a variable like let redirect = null and if !this.props.auth.isAuthenticated is correct, set this redirect variable to redirect = <Redirect to="/" /> (If you use browser routing!) and use this variable like this,
render() {
return (
{redirect}
)
}

How to pass form data from angular to nodejs

I am new to Angular5. I need to pass user details from angular to nodejs.
app.component.ts:
import { Component } from '#angular/core';
import { FormBuilder, FormGroup, Validators, FormControl, FormArray } from
'#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private http:Http) { }
onSubmit(registerForm) {
console.log(registerForm.value);
let url = 'http://localhost:8080/signup';
this.http.post(url, {registerForm(registerForm)}).subscribe(res =>
console.log(res.json()));
}
}
Now I need to pass those data to nodejs routes to proceed further.
Node js routing file:
module.exports = function(app, passport) {
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/',
failureRedirect : '/',
failureFlash : true
}));
};
Now am getting the following error: Uncaught Error: Can't resolve all parameters for AppComponent: (?).
Call Your function from the component.html file it will trigger the function which will be in your component.ts file.
From this function call service which contains the function which will be requesting your node API
addData() {
this.adminService.addCountry(this.form.value).subscribe(
res => {
var response = res.json();
this.flashMessagesService.show(response.message, {
cssClass: "alert-success",
timeout: 2000
});
},
error => {
if (error.status == 401) {
localStorage.removeItem("currentUser");
this.router.navigate(["/"]);
} else {
this.flashMessagesService.show(error.json().error, {
cssClass: "alert-danger",
timeout: 2000
});
}
}
);
}
Create admin service to call your HTTP URL which is running on node
Service
addCountry(formData) {
console.log(formData);
var authToken = this.getAuthToken();
if (authToken != "") {
var headers = this.getHeaders();
headers.append("Authorization", authToken);
return this.http
.post(
`http://localhost:3000/addData`,
this.formData(formData),
{ headers: headers }
)
.map((response: Response) => {
return response;
});
}
}
You can use service in angular to send data to nodeJs. Please refer the tutorials of Angular from Codecraft. Please have a look at https://codecraft.tv/courses/angular/http/core-http-api/
For now you need to send some registration form data. So
1. import http module to AppModule
2. Refer to the documentation above
3. You can pass data to nodejs using a POST method of http
I think you should look on Observable.
https://angular.io/guide/observables
On logic you should create server with Observable request to your NodeJs (express) app. Then you can add to your component function with subscribe.
Some code:
Create authentication service
ng generate service authentication
Create user service for store user data (or you can only store it in other components)
ng generate service user
On authentication.service.ts create authenticate method
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { UserService } from '../user/user.service';
import { Router } from '#angular/router';`
#Injectable()
export class AuthenticationService {
token: string;
constructor(private router: Router, private httpClient: HttpClient,
public userService: UserService) {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
this.token = currentUser && currentUser.token;
}
getToken(email: string, password: string): Observable<User> {
return this.httpClient.post<User>(apiRoutes.authentication,
{userEmail: email, userPassword: password});
}
authenticate(email: string, password: string) {
this.getToken(email, password).subscribe(response => {
if (response.userToken.length > 0) {
this.userService.user.userEmail = response.userEmail;
this.userService.user.userToken = response.userToken;
this.userService.user._id = response._id;
this.userService.user.isUserAuthenticated = true;
localStorage.setItem('currentUser', JSON.stringify({token: response.userToken}));
this.router.navigate(['/']);
// TODO: Need some error logic
} else {
return false;
}
});
}
Now you can add to your form in template
<form (ngSubmit)="this.authenticationService.authenticate(userEmail, password)">
...
</form>

How to display data from api in react component

I am using axios to get data from an API and the retrieved data was successfully dispatched to the store, I currently have some problems in displaying the data in my React component.
I was able to console.log the data successfully but it turns out my component renders before the data was fully retrieved from the API.
Below is the component to display the data:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import HeaderSidebar from './includes/header-navbar';
import AllBooks from './includes/all-books';
import { getAllBooks } from '../../actions/book_actions';
class AdminHome extends Component {
componentDidMount() {
this.props.actions.getAllBooks();
}
renderBooks(){
const allbooks = this.props.books;
return allbooks.map((book) => {
return (<div key={book.id}>
<AllBooks title={book.title}
description={book.description}
/>
</div>)
})
}
render() {
return (
<div >
<HeaderSidebar />
{this.renderBooks()}
})}
})}
</div >
)
}
}
function mapStateToProps(state) {
return {
books: state.book.data
}
}
AdminHome.PropTypes = {
books: PropTypes.object.isRequired
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
getAllBooks
}, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AdminHome);
Actions
export function getAllBooks() {
return dispatch => axios.get(`${API_URL}`)
.then((res) => {
dispatch({
type: GET_ALL_BOOKS,
data: res.data
});
return res.data;
})
.catch(error => error);
}
Reducers
import { ADD_BOOK,
GET_ALL_BOOKS
} from '../actions/types';
const INITIAL_STATE = {};
function bookReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_BOOK:
return { ...state, message: 'Book added Successfully' };
case GET_ALL_BOOKS:
return { ...state, data: action.data };
default:
return state;
}
}
export default bookReducer;
You can show something else until the data is done loading
renderBooks(){
if(!this.props.books) {
return (<p>Loading data..<p/>);
}
// rest of your code here
Or you can initialize the property with an empty array so that at least nothing crashes until the data is done loading.
function mapStateToProps(state) {
return {
books: state.book.data || [] // maybe have to extra checks here if state.book doesn't exist yet
}
(Or do both, and change if(!this.props.books) to if(this.props.books.length < 1).

Resources