I have read a lot of threads about my issue but none of them helped me.
I developed a JWT authentication app using React.JS and Redux with a django rest framework API as backend.
User information is sent to authenticated user through a GET request :
{
"id": 1,
"email": "",
"first_name": "",
"last_name": ""
}
user.actions.js (redux action)
export const loadUser = () => {
return (dispatch) => {
const token = localStorage.getItem('token');
if (token) {
axios.get('http://localhost:8000/auth/user/', {
headers: { 'Authorization': 'token ' + token }
})
.then(res => {
dispatch({ type: USER_LOADED, data: JSON.stringify(res.data) })
}).catch((error) => {
// catch later
})
}
};
};
auth.reducers.js
import {
SIGNIN_REQUEST,
SIGNIN_SUCCESS,
SIGNIN_FAILURE,
SIGNOUT,
USER_LOADED
} from '../_constants/user.constants';
const initialState = { }
export function auth(state = {}, action) {
switch (action.type) {
case SIGNIN_REQUEST:
return { ...state, isLogging: true }
case SIGNIN_SUCCESS:
return { ...state, isLogged: true }
case SIGNIN_FAILURE:
return { ...state, error: action.data, isLogged: false, isLogging: false }
case USER_LOADED:
return { ...state, isLogged: true, user: action.data }
case SIGNOUT:
return { }
default:
return state;
}
};
App.js
class App extends Component {
componentDidMount() {
let token = localStorage.getItem('token');
if (token) {
this.props.loadUser();
}
}
render() {
return (
<div className="container-scroller">
<BrowserRouter>
<Router history={History}>
<Switch>
...
</Switch>
</Router>
</BrowserRouter>
</div>
);
}
}
const mapStateToProps = state => {
return {
auth: state.auth
}
}
const mapDispatchToProps = dispatch => {
return {
loadUser: () => {
return dispatch(userActions.loadUser());
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Everything works fine. The data is correctly fetch. When I write {this.props.auth.user} in my render method, it displays the data correctly (as sent by the API) :
{"id":1,"email":"","first_name":null,"last_name":null}
But when I try to display a value of the data (id, email, ...) it shows an error : (with map or this.props.auth.user['id']
TypeError: this.props.auth.user is undefined
How can I display the data properly ?
Thanks in advance :)
Related
I'm having trouble fetching a list of users from an api. I think issue might be in my mapDispatchToProps function but I'm not sure. Everything else seems fine to me. I'm new to redux and I'm kinda having a hard time wrapping my head around it so any help is appreciated
The list with the users would ideally be displayed as soon as the component mounts. I did the same thing without redux store and it was working just fine, I'm just not really sure how to integrate redux
Actions
export const startLoading = () => {
return {
type: START_LOADING
}
}
export const updateUserData = payload => {
return {
type: UPDATE_USER_DATA,
payload
}
}
export const updateUserError = payload => {
return {
type: UPDATE_USER_ERROR,
payload: payload
}
}
export function fetchUsers() {
return dispatch => {
dispatch(startLoading());
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => {
data = data.filter(user => user.id < 4);
data.forEach(user => {
user.isGoldClient = false;
user.salary = '4000';
user.photo = userThumbnail;
})
.then(data => {
dispatch(updateUserData(data));
}).catch(error => {
dispatch(updateUserError(error));
})
});
};
};
Reducers
const initialState = {
loading: false,
users: [],
error: null
};
export function userReducer(state=initialState, action){
switch(action.type){
case START_LOADING:
return {
...state,
loading: true
}
case UPDATE_USER_DATA:
return {
...state,
loading: false,
users: action.payload,
error: null
}
case UPDATE_USER_ERROR:
return {
...state,
error: action.payload,
loading: false,
users: []
};
default:
return state;
};
};
Component
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [],
usersAreDisplayed: true
};
}
componentDidMount() {
fetchUsers();
}
render(){
return (
<UserList users={this.state.users} />
)
}
}
function mapStateToProps(state){
return { users: state.users }
}
function mapDispatchToProps(dispatch){
return {
fetchUsers: payload => dispatch(updateUserData(payload)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Looks like you are not calling the actual fetchUsers at all.
Change the component code like this
function mapStateToProps(state){
return { users: state.users }
}
// remove this function
// function mapDispatchToProps(dispatch){
// return {
// fetchUsers: payload => dispatch(updateUserData(payload)),
// }
// }
export default connect(mapStateToProps, {fetchUsers})(Home); //<---- destructure it here. Also import the function (action)
1a. fetchUsers function needs to be accessed using this.props
componentDidMount() {
this.props.fetchUsers();
}
There is an extra then block after forEach.
Remove it.
export function fetchUsers() {
return (dispatch) => {
dispatch(startLoading());
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => {
data = data.filter((user) => user.id < 4);
data.forEach((user) => {
user.isGoldClient = false;
user.salary = "4000";
user.photo = userThumbnail;
});
dispatch(updateUserData(data)); // <------ no extra .then is required
})
.catch((error) => {
dispatch(updateUserError(error));
});
};
}
Also <UserList users={this.state.users} /> needs to be <UserList users={this.props.users} /> As already mentioned by #Nsevens
You are mapping redux state into your component's props.
So you should load the users from the component's props and not it's state:
render(){
return (
<UserList users={this.props.users} />
)
}
I'm trying to implement authentication with Python and React, and I have this error message on the front-end.
TypeError: Cannot read property 'loading' of undefined
And this is my SignIn.js
import React, { Component } from "react";
import { Button, Checkbox, Form, Icon, Input } from "antd";
import { Link, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import { authLogin } from "../store/actions/auth";
class SignIn extends React.Component {
state = {
username: "",
password: ""
};
handleChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
handleSubmit = e => {
e.preventDefault();
const { username, password } = this.state;
this.props.login(username, password);
};
render() {
const { getFieldDecorator } = this.props.form;
const { error, loading, token } = this.props;
const { username, password } = this.state;
if (token) {
return <Redirect to="/" />;
}
return (
<div className="gx-login-container">
<div className="gx-login-content">
<div className="gx-login-header gx-text-center">
<h1 className="gx-login-title">Sign In</h1>
</div>
{error && <p>{this.props.error.message}</p>}
<React.Fragment>
<Form onSubmit={this.handleSubmit} className="gx-login-form gx-form-row0">
{getFieldDecorator('email', {
rules: [{ required: true, message: 'Please input your email!' }],
})(
<Button type="primary" htmlType="submit" loading={loading} disabled={loading}>
Log in
</Button>
</Form>
</React.Fragment>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
token: state.auth.token
};
};
const mapDispatchToProps = dispatch => {
return {
login: (username, password) => dispatch(authLogin(username, password))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(SignIn);
I have deleted the input part because I don't think that there is some problem. If someone think that the input part is the problem I will gladly post it.
Here is my reducers/auth.js
import * as actionTypes from "../actions/actionTypes";
import { updateObject } from "../utility";
const initialState = {
token: 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.token,
error: null,
loading: false
});
};
const authFail = (state, action) => {
return updateObject(state, {
error: action.error,
loading: false
});
};
const authLogout = (state, action) => {
return updateObject(state, {
token: null
});
};
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);
case actionTypes.AUTH_LOGOUT:
return authLogout(state, action);
default:
return state;
}
};
export default reducer;
The errors says that it cannot find the property loading in an undefined object. Maybe your state.auth is null or undefined. Try logging the state.auth to check if it has a value.
I think I got your issue. You have initial state as,
const initialState = {
token: null,
error: null,
loading: false
};
and you are trying to get the state in mapStateToProps as,
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
token: state.auth.token
};
};
Here you are trying to access state using state.auth.loading, but you don't have auth object in your initial state and you get undefined error. You can fix this like,
const mapStateToProps = state => {
return {
loading: state.loading,
error: state.error,
token: state.token
};
};
Note: If your updateObject function returning state with auth object then you need to correct that. Your initial state and returned state should be of same pattern.
I am new to the react-redux. Now here what I am doing is ,
In this, On click of login , and on the success of this ,
history.push('/');
I am redirecting user on this page.
Now,
import React from 'react';
import { fetchUserJd } from '../action/index';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import JobList from '../../JobList/container/JobList';
class LandingPage extends React.Component {
componentDidMount() {
this.props.fetchJobDescription();
}
render() {
if (this.props.jobs && this.props.jobs.content && this.props.jobs.content.length > 0) {
return <JobList />;
}
else {
return <Redirect to="/create-job" />
}
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchJobDescription: () => dispatch(fetchUserJd())
}
}
const mapStateToProps = (state) => {
return {
jobs: state.UserJobs.response
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LandingPage);
Here this is my landing page.
Now, Here what I do is I call an api in the componentDidMount and get some data. If data is avaliable then I redirect user to some another page or to some diff route.
const initialState = {
response: [],
error: false
}
case FETCHING_JOBDESCRIPTION_SUCCESS:
return {
...state,
response: action.data,
error: false,
}
This is my reducer where I used the initial state as an empty array.
Now Here what is happening, it is directly going to the else condition to the create-job but the data is avaliable so it should have rendered that component. But, Somehow it is not happening.
Response is like =>
{
"content": [{
"id": "5b7d4a566c5fd00507501051",
"hrmsJdId": null,
"companyId": null,
"jdName": "Senior/ Lead UI Developer",
"jobDescription": null,
}]
}
render() {
if (this.props.isFetching) {
return null;
}
else if (this.props.jobs && this.props.jobs.content && this.props.jobs.content.length > 0) {
return <JobList />;
}
else {
return <Redirect to="/create-job" />
}
}
}
The solution that I used is like this ,
export function fetchUserJd() {
return (dispatch) => {
let url = FETCH_JD_ROOT_URL + page + "&" + size;
dispatch({
type: REQUEST_INITIATED
})
return get(url)
.then((response) => {
if (response.status === 200) {
dispatch(
{
type: FETCHING_JOBDESCRIPTION_SUCCESS,
data: response.payload
}
)
dispatch({
type: REQUEST_SUCCESSED
})
} else {
dispatch({
type: REQUEST_SUCCESSED
})
}
})
}
return Promise.resolve();
};
So, Here I used Promise.resolve Now, in the componentDidMount,
in constructure,
constructor(props) {
super(props);
this.state = {
isloading: true
}
}
componentDidMount() {
this.props.fetchJobDescription().then(() => {
this.setState({
isloading: false
})
});
}
SO, can any one please help me with this issue?
I'm fairly new to React Native and Redux and was following along with this tutorial to implement Redux with my app. When I load my PostList component, I get the following warnings for my hasError and isLoading catches as shown in the following screenshots.
I apologize for the massive amounts of source code about to be embedded in the question, I tried to cut out unnecessary code.
PostList.js (Component)
[unnecessary import statements]
import { fetchPosts, postsFetchError, postsFetchLoading } from '../actions/PostsActions';
class PostList extends Component {
static navigationOptions = ({navigation}) => {
[redacted]
}
renderPosts = ({ _id, upvotes, downvotes, message, datetime }, i) => {
[redacted]
}
componentDidMount() {
this.props.fetchData('[redacted]');
}
render() {
if (this.props.hasError) {
return (
<View style={styles.center}>
<Text>
Failed to load posts!
</Text>
</View>
)
}
if (this.props.isLoading) {
return (
<View style={styles.center}>
<ActivityIndicator animating={true} />
</View>
)
}
this.props.posts.sort(function(a,b) {
return Date.parse(b.datetime) - Date.parse(a.datetime);
})
return (
<ScrollView style={styles.container}>
{this.props.posts.map(this.renderPosts)}
</ScrollView>
)
}
}
PostList.propTypes = {
fetchData: PropTypes.func.isRequired,
posts: PropTypes.array.isRequired,
hasError: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
const mapStateToProps = (state) => {
return {
posts: state.posts,
hasError: state.postsFetchError,
isLoading: state.postsFetchLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (url) => dispatch(fetchPosts(url)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(PostList);
PostsActions.js
import axios from 'axios';
export function postsFetchError(bool) {
return {
type: 'POSTS_FETCH_ERROR',
hasError: bool
};
}
export function postsFetchLoading(bool) {
return {
type: 'POSTS_FETCH_LOADING',
isLoading: bool
};
}
export function postsFetchSuccess(posts) {
return {
type: 'POSTS_FETCH_SUCCESS',
posts
};
}
export function fetchPosts(url) {
return (dispatch) => {
dispatch(postsFetchLoading(true));
axios.get(url)
.then((response) => {
if (response.status !== 200) {
throw Error(response.statusText);
}
dispatch(postsFetchLoading(false));
return response;
})
.then((response) => dispatch(postsFetchSuccess(response.data)))
.catch(() => dispatch(postsFetchError(true)));
};
}
PostsReducers.js
export function postsError(state = false, action) {
switch (action.type) {
case 'POSTS_FETCH_ERROR':
return action.hasError;
default:
return state;
}
}
export function postsLoading(state = false, action) {
switch (action.type) {
case 'POSTS_FETCH_LOADING':
return action.isLoading;
default:
return state;
}
}
export function posts(state = [], action) {
switch (action.type) {
case 'POSTS_FETCH_SUCCESS':
return action.posts;
default:
return state;
}
}
Combining the reducers in an index and configuring the store all work fine, and I don't get warnings for actions and reducers that actually get the posts. My code matches the tutorial's examples, but I'd be shocked if someone published a tutorial that had unresolved warnings (then again this IS Javascript development so I guess anything goes). I can add further information for anyone that's curious. Thanks to anyone that helps.
Is it because:
const mapStateToProps = (state) => {
return {
posts: state.posts,
hasError: state.**postsFetchError**,
isLoading: state.**postsFetchLoading**
};
};
Does not match:
export function **postsError**(state = false, action) {
export function **postsLoading**(state = false, action)
I've read many examples about this and got no result for my problem, I want to get the values inside MySQL database by using localhost, code with PHP and return the value as JSON format e.g.
[
{"id":"7",
"name":"Sammy",
"address":"New York",
"age":"42"}
]
with this format, I can fetch the data by using this code in GetApi.js
class GetApi {
static getAllUsers() {
return fetch('http://192.168.1.199/App/show_all_data.php')
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(itemsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((items) => dispatch(itemsFetchDataSuccess(items)))
.catch(() => dispatch(itemsHasErrored(true)));
}
}
export default GetApi;
here's the action.js
import GetApi from '../../api/GetApi';
export function itemsHasErrored(bool: boolean) {
return {
type: "ITEMS_HAS_ERRORED",
hasErrored: bool
};
}
export function itemsIsLoading(bool: boolean) {
return {
type: "ITEMS_IS_LOADING",
isLoading: bool
};
}
export function itemsFetchDataSuccess(items: Object) {
return {
type: "ITEMS_FETCH_DATA_SUCCESS",
items
};
}
export function itemsFetchData(url: any) {
return function(dispatch) {
return GetApi.getAllUsers().then(items => {
dispatch(itemsFetchDataSuccess(items));
dispatch(itemsIsLoading(false));
}).catch(error => {
throw(error);
});
};
}
here's the reducer.js
const initialState = {
isLoading: true,
hasErrored: false,
items: []
};
export default function(state: any = initialState, action: Function) {
switch (action.type) {
case "ITEMS_HAS_ERRORED":
return { ...state, hasErrored: action.hasErrored };
case "ITEMS_IS_LOADING":
return { ...state, isLoading: action.isLoading };
case "ITEMS_FETCH_DATA_SUCCESS":
return { ...state, items: action.items };
default:
return state;
}
}
called action.js function in index.js
import { itemsFetchData } from "../../actions";
...
all codings that were not related with calling action.js
...
const navigation = this.props.navigation;
let items = this.props.items;
if (items.hasOwnProperty('item')) {
items = items.item
}
return (
<List
dataArray={this.props.items}
renderRow={(
data
) =>
<ListItem icon style={styles.listitem}>
<Left>
<Text>
{data.name}
</Text>
</Left>
<Right>
<Text>
{data.address}
</Text>
</Right>
</ListItem>}
/>
);
function bindAction(dispatch) {
return {
fetchData: url => dispatch(itemsFetchData(url))
};
}
const mapStateToProps = state => ({
items: state.homeReducer.items,
hasErrored: state.homeReducer.hasErrored,
isLoading: state.homeReducer.isLoading
});
export default connect(mapStateToProps, bindAction)(ShowData);
I got no results when I'm running the code, it's just showed the loading icon. even when I set isLoading:false, the home menu showed up without the data
I'm just trying to minimize the code inside index.js because it's too long to post that here. I will do that if necessary in the next comment.
I recommend using epics , below is an example link for you to follow.
Epic Example
You can look at the actions and data ajax calls from epic and how it connects back to the action.
Note: Axios is been used here instead of fetch api...