I'm using React JS with Redux and have a little problem retrieving data from a request with Axios..
Here is my Axios request :
import Axios from 'axios';
class UsersApi {
static getAllUsers() {
return Axios.get('http://localhost:3001/user').then(response => {
return response;
});
}
}
export default UsersApi;
And this is where i want to use my data :
import React, {Component} from 'react';
import {connect} from 'react-redux';
class UserList extends Component {
render(){
console.log(this.props.users);
return(
<ul>
</ul>
);
}
}
function mapStateToProps(state) {
return {
users: state.users
};
}
export default connect(mapStateToProps)(UserList);
This is the action :
import UsersApi from '../api/UsersApi';
export function loadUsers() {
return function(dispatch) {
return UsersApi.getAllUsers().then(users => {
dispatch(loadUsersSuccess(users));
}).catch(error => {
throw(error);
});
};
}
export function loadUsersSuccess(users) {
return {type: 'LOAD_USERS_SUCCESS', users};
}
And this is my reducer :
import initialState from './initialState';
export default function usersReducer(state = initialState.users, action) {
switch(action.type) {
case "LOAD_USERS_SUCCESS":
return action.users
default:
return state;
}
}
And this is what i have with console.log : Result console.log
When i try to display the password of the first user i put console.log(this.props.users.data["0"].pwd) but this is not working..
If I try to return response.data["0"].pwd in the request I can have the password with console.log(this.props.users).
But the problem is that i want the data of every User..
I need some help. :)
First of all, you need to add your users to your state. I see you are using redux, so you should dispatch an action after receiving your users and let your reducers handle it:
Axios.get('http://localhost:3001/user').then(response => {
dispatch(usersLoaded(response.data));
});
You should have an action called usersLoaded and a reducer that handles it:
const usersLoaded = users => ({ type: 'USERS_LOADED', payload: users });
const users = (state = [], action) => {
switch(action.type) {
case 'USERS_LOADED':
return action.payload;
default:
return state;
}
}
This way you'll be able to access your users in your UserList component
As a proper redux way to do things, right inside the then on your get call you need to fire an action to SET_USERS in which you will use your reducer to replace your state with your new users coming back from the api.
Related
I made a reducer that fetches admins, and I want it to display certain admins when I call it in my reducer but I am getting Undefined.
I am still very new to redux so apologies for my mistakes.
I tried to include all the relevant folders:
App.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../store/actions';
class App extends Component {
async componentDidMount() {
fetch(constants.adminUrl + '/admins/data', {
method: 'GET'
}).then((res) => {
return res.json()
}).then(async (res) => {
this.props.setAdminsInColumns(res.admins)
}).catch((error) => {
toast.error(error.message)
})
}
render() {
return (
{/* SOME CODE */}
);
}
}
let app = connect(null, actions)(App);
export default app;
columnsReducer.js
import { FETCH_ADMINS } from '../actions/types'
import { Link } from 'react-router-dom'
import constants from '../../static/global/index'
import React from 'react';
import { toast } from 'react-toastify'
const initialState = {
admins: [],
{
Header: "Responsible",
accessor: "responsibleAdmin",
style: { textAlign: "center" },
// Place where I want to fetch certain admins and get undefined
Cell: props => <span>{props.value && this.state.admins.name ? this.state.admins.find(admin => admin.id === props.value).name : props.value}</span>
}
}
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_ADMINS:
return { ...state, admins: action.admins}
default:
return state
}
}
index.js
import { FETCH_ADMINS } from "./types"
/**
* starts loader for setting admin
*/
export const setAdminsInColumns = (admins) => async dispatch => {
dispatch({ type: FETCH_ADMINS, admins })
}
types.js
export const FETCH_ADMINS = 'fetch_admins'
When I console.log(action.admins) inside the switch case FETCH_ADMINS in the columnsReducer.js file, I can see all the admin information I want, is there a way to make the state global in the columnsReducer.js file so I can read it?
Any help is appreciated!
use mapStateToProps in the connect method. like below
let mapStateToProps = (state)=>{
return {
admins :[yourcolumnsReducer].admins
}
}
let app = connect(mapStateToProps, actions)(App);
//you can use this.props.admins inside your component
MapStateToProps reference
I'm new to React/Redux. I'm making an app using an API but the code doesn't work. When I run the code it says "this.props.recipes.map is not a function" and doesn't render anything.
If I change payload to: "payload: response.data.recipes" then the error changes to "Given action "FETCH_RECIPE", reducer "recipes" returned undefined." but no errors on screen (only in console). I thought writing "(state = [], action)" would solve the problem but it seems not. What's the problem and how do I fix this error?
Action Creator
import recipe from '../apis/recipe';
export const fetchRecipe = () => async dispatch => {
const response = await recipe.get('');
dispatch({ type: 'FETCH_RECIPE', payload: response.data })
};
Reducer
import { combineReducers } from 'redux';
const recipeReducer = (state = [], action) => {
switch(action.type) {
case 'FETCH_RECIPE':
return action.payload;
default:
return state;
}
};
export default combineReducers({
recipes: recipeReducer
});
import React from 'react';
import { connect } from 'react-redux';
import { fetchRecipe } from '../actions';
class Recipe extends React.Component {
componentDidMount() {
this.props.fetchRecipe();
console.log("This doesn't work", this.props.recipes)
}
renderList() {
return this.props.recipes.map(recipe => {
return (
<div>
<p>{recipe.publisher}</p>
</div>
)
})
}
render() {
console.log("First loaded: empty, second time: data fetched", this.props.recipes)
return (
<div>
{this.renderList()}
</div>
);
}
}
const mapStateToProps = (state) => {
return { recipes: state.recipes }
};
export default connect(mapStateToProps,{
fetchRecipe
})(Recipe);
API Request
import axios from 'axios';
import { key } from './config';
export default axios.create({
baseURL: `https://cors-anywhere.herokuapp.com/https://www.food2fork.com/api/search?key=${key}&q=pizza`
});
I'm wanting to update my trending array with the results calling the tmdb api. I'm not sure if im going about this the wrong way with calling the api or if im messing up somewhere else along the way. So far I've really been going in circles with what ive tried. Repeating the same things and not coming to a real solution. Havent been able to find another question similar to mine.
my actions
export const getTrending = url => dispatch => {
console.log("trending action");
axios.get(url).then(res =>
dispatch({
type: "TRENDING",
payload: res.data
})
);
};
my reducer
const INITIAL_STATE = {
results: [],
trending: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case "SEARCH_INFO":
return {
results: [action.payload]
};
case "TRENDING":
return { trending: action.payload };
default:
return state;
}
};
and my component im trying to get the results from
import React, { Component } from "react";
import Trending from "./Treding";
import "../App.css";
import { getTrending } from "../actions/index";
import { connect } from "react-redux";
export class Sidebar extends Component {
componentDidMount = () => {
const proxy = `https://cors-anywhere.herokuapp.com/`;
getTrending(`${proxy}https://api.themoviedb.org/3/trending/all/day?api_key=53fbbb11b66907711709a6f1e90fc884
`);
};
render() {
return (
<div>
<h3 className="trending">Trending</h3>
{
this.props.trending ? (
<Trending movies={this.props.trending} />
) : (
<div>Loading</div>
)}
</div>
);
}
}
const mapStateToProps = state => {
return {
trending: state.trending
};
};
export default connect(mapStateToProps)(Sidebar);
Since you are directly calling the getTrending without passing it to connect method, it might be the issue.
Instead that you can pass getTrending to connect method so it can be available as props in the component. After that it can be dispatched and it will be handled by redux/ redux-thunk.
export default connect(mapStateToProps, { getTrending })(Sidebar);
And access it as props in the component.
componentDidMount = () => {
// const proxy = `https://cors-anywhere.herokuapp.com/`;
this.props.getTrending(`https://api.themoviedb.org/3/trending/all/day?api_key=53fbbb11b66907711709a6f1e90fc884
`);
};
Basically what I wanted to do was to stop making axios calls inside of my component. So I thought; “Why not just create an action for that?”
I googled around to find a good “guide” to use Redux and this is what I’m using:
Add a constant to the constants file. Something like const GREAT_COURSE = GREAT_COURSE
Add an action creator to the actions folder. Return an action JavaScript object with a type of the constant you created.
Add a reducer to the reducers folder that handles this action creator.
So I began to create my action creator:
import axios from 'axios'
import { CUSTOMER_FETCH } from './types'
import settings from '../settings'
axios.defaults.baseURL = settings.hostname
export const customers = () => {
return dispatch => {
return axios.get('http://hejhej/customers').then(res => {
dispatch({
type: CUSTOMER_FETCH,
data: res.data
})
})
}
}
And later to add a reducer that handles my action creator:
import { CUSTOMER_FETCH } from '../actions/types'
const initial = []
const customer = action => {
return {
data: action.data
}
}
const customers = (state = initial, action) => {
switch (action.type) {
case CUSTOMER_FETCH:
customers = [...state, customer(action)]
console.log('customers as state', customers)
return customers
default:
return state
}
}
export default customers
And inside of my component I'm importing it:
import { customers } from '../../actions/customersAction'
And later using connect: export default connect(null, { customers })(Events)
And finally I'm using it inside of my component:
customers() {
this.props.customers(this.state.data)
}
So I'm wondering what I'm doing wrong, because I can't see my console.log in my dev tools. Thanks a lot for reading!
Inside of my component atm:
axios.get('http://hejhej/customers').then(res => {
this.setState({
res,
customer: res.data
})
})
I am new to react native + redux. I have an react native application where user first screen is login and after login am showing page of list of categories from server. To fetch list of categories need to pass authentication token, which we gets from login screen or either if he logged in previously then from AsyncStorage.
So before redering any component, I am creating store and manully dispatching fetchProfile() Action like this.
const store = createStore(reducer);
store.dispatch(fetchProfile());
So fetchProfile() try to reads profile data from AsyncStorage and dispatch action with data.
export function fetchProfile() {
return dispatch => {
AsyncStorage.getItem('#myapp:profile')
.then((profileString) => {
dispatch({
type: 'FETCH_PROFILE',
profile: profileString ? JSON.parse(profileString) : {}
})
})
}
}
so before store get populated, login page get rendered. So using react-redux's connect method I am subscribing to store changes and loading login page conditionally.
class MyApp extends React.Component {
render() {
if(this.props.profile)
if(this.props.profile.authentication_token)
retunr (<Home />);
else
return (<Login />);
else
return (<Loading />);
}
}
import { connect } from 'react-redux';
const mapStateToProps = (state) => {
return {
profile: state.profile
}
}
module.exports = connect(mapStateToProps, null)(MyApp);
So first 'Loading' component get rendered and when store is populated then either 'Login' or 'Home' component get rendered. So is it a correct flow? Or is there a way where I can get store populated first before any compnent render and instead of rendering 'Loading' component I can directly render 'Login' or 'Home' Component.
Verry common approach is to have 3 actions for an async operation
types.js
export const FETCH_PROFILE_REQUEST = 'FETCH_PROFILE_REQUEST';
export const FETCH_PROFILE_SUCCESS = 'FETCH_PROFILE_SUCCESS';
export const FETCH_PROFILE_FAIL = 'FETCH_PROFILE_FAIL';
actions.js
import * as types from './types';
export function fetchProfile() {
return dispatch => {
dispatch({
type: types.FETCH_PROFILE_REQUEST
});
AsyncStorage.getItem('#myapp:profile')
.then((profileString) => {
dispatch({
type: types.FETCH_PROFILE_SUCCESS,
data: profileString ? JSON.parse(profileString) : {}
});
})
.catch(error => {
dispatch({
type: types.FETCH_PROFILE_ERROR,
error
});
});
};
}
reducer.js
import {combineReducers} from 'redux';
import * as types from './types';
const isFetching = (state = false, action) => {
switch (action.type) {
case types.FETCH_PROFILE_REQUEST:
return true;
case types.FETCH_PROFILE_SUCCESS:
case types.FETCH_PROFILE_FAIL:
return false;
default:
return state;
}
};
const data = (state = {}, action) => {
switch (action.type) {
case types.FETCH_PROFILE_SUCCESS:
return action.data;
}
return state;
};
export default combineReducers({
isFetching,
data
});
So you can get isFetching prop in your component and show/hide Loader component
You can load all your data during the splash screen and then load the others screens after that. I did it like this. Hope it helps
class Root extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
store: configureStore( async () => {
const user = this.state.store.getState().user || null;
if (categories && categories.list.length < 1) {
this.state.store.dispatch(categoriesAction());
}
this.setState({
isLoading: false
});
}, initialState)
};
}
render() {
if (this.state.isLoading) {
return <SplashScreen/>;
}
return (
<Provider store={this.state.store}>
<AppWithNavigationState />
</Provider>
);
}
}
Redux and Redux Persist (https://github.com/rt2zz/redux-persist) will solve your problem.
Don't make them complex.