I have a component which binds actions with it's props like this
I am not including the complete api call code in the handleButtonclick function here to avoid unnecessary code.
class LogInComponent extends Component {
static contextTypes = {
router: PropTypes.object.isRequired
}
handleLoginButtonClick() {
let token;
$.ajax(settings).done((response) => {
token = JSON.stringify(response.auth_token)
this.props.setAuthToken(token);
this.context.router.push('/app')
});
}
render(){
return (
<div className="LoginPage">
<button onClick={this.handleLoginButtonClick.bind(this)}>login</button>
</div>
);
}
}
const mapStateToProps = (state)=> ({
auth_token: state.Data.auth_token
})
function matchDispatchToProps(dispatch) {
return bindActionCreators({setAuthToken: actions.setAuthToken}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(LogInComponent);
This is where my action and reducer are there:
import fetch from 'isomorphic-fetch'
const INITIAL_STATE = {
list: [],
selectedRows: [],
currentItem: {},
auth_token:null
}
const FETCH_LIST = 'FETCH_LIST'
const fetchList = ()=> (dispatch)=> {
dispatch({type: FETCH_LIST});
fetch('/api/items?n=50')
.then(resp => resp.json())
.then(data => dispatch(fetchListSuccess(data)))
.catch(err => dispatch(fetchListError(err)))
}
const FETCH_LIST_SUCCESS = 'FETCH_LIST_SUCCESS'
const fetchListSuccess = (list)=> {
console.log('Received List: ', list)
return {
type: FETCH_LIST_SUCCESS,
list
}
}
const FETCH_LIST_ERROR = 'FETCH_LIST_ERROR'
const fetchListError = (error)=> {
console.error(error)
return {
type: FETCH_LIST_ERROR,
error: error.message
}
}
const SELECT_ROWS = 'SELECT_ROWS'
const selectRows = (ids)=> {
return {
type: SELECT_ROWS,
ids
}
}
const SET_AUTH_TOKEN = 'SELECT_ROWS'
const setAuthToken = (token)=> {
return {
type: SET_AUTH_TOKEN,
payload: token
}
}
const SET_CURRENT_ITEM = 'SET_CURRENT_ITEM'
const setCurrentItem = (item)=> {
return {
type: SET_CURRENT_ITEM,
item
}
}
export const actions = {
fetchList,
selectRows,
setCurrentItem,
setAuthToken
}
export default function DataReducer(state = INITIAL_STATE, action){
switch(action.type){
case FETCH_LIST:
return {...state, isLoading: true }
case FETCH_LIST_SUCCESS:
return { ...state, isLoading: false, list: [...action.list] }
case FETCH_LIST_ERROR:
return {...state, isLoading: false, hasError: action.error }
case SELECT_ROWS:
return {...state, selectedRows: [...action.ids]}
case SET_CURRENT_ITEM:
return {...state, currentItem: {...action.item}}
case SET_AUTH_TOKEN:
return {...state, auth_token:action.payload}
default:
return state
}
}
I am getting an error like this
Where am I going wrong?
Related
isAuthenticated is undefined when i run this code. how can is use isAuthenticated with mapStateProps. if i am use Token `(Token '5302f4340a76cd80a855286c6d9e0e48d2f519cb'} like this then it's working fine but i want Authorized it with props.isAuthenticated anybody know how can i solve this issue?
authAction.js
import axios from 'axios';
import * as actionTypes from './actionTypes';
export const authStart = () => {
return {
type: actionTypes.AUTH_START
}
}
export const authSuccess = token => {
return {
type: actionTypes.AUTH_SUCCESS,
token: token
}
}
export const authFail = error => {
return {
type: actionTypes.AUTH_FAIL,
error: error
}
}
export const logout = () => {
localStorage.removeItem('token');
return {
type: actionTypes.AUTH_LOGOUT
};
}
export const authLogin = (userData) => {
return dispatch => {
dispatch(authStart());
axios.post('http://localhost:8000/rest-auth/login/', userData)
.then(res => {
const token = res.data.key;
localStorage.setItem('token', token);
dispatch(authSuccess(token));
})
.catch(err => {
dispatch(authFail(err))
})
}
}
authReducer.js
import * as actionTypes from '../actions/actionTypes';
import { updateObject } from '../utility';
const initialState = {
isAuthenticated: null,
token: null,
error: null,
loading: false
}
const authStart = (state, action) => {
return updateObject(state, {
isAuthenticated: false,
error: null,
loading: true
});
}
const authSuccess = (state, action) => {
return updateObject(state, {
isAuthenticated: true,
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
});
}
export default function (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;
}
}
index.js
import { combineReducers } from 'redux';
import auth from './authReducer'
export default combineReducers({
auth: auth
});
articleList.js
const NewsList = (props) => {
// ...
const fetchItems = async () => {
try {
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Token ${props.isAuthenticated}`
}
}
const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/`, config);
setItems(res.data)
setLoading(false);
}
catch (err) {
console.log(`😱 Axios request failed: ${err}`);
}
}
fetchItems()
})
}, [items]);
// ...
}
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.token
}
}
export default connect(mapStateToProps)(NewsList)
You need to debug your code. Start by connecting the dots: The output tells you that props.isAuthenticated is undefined. You pass this in from state.auth.token in mapStateToProps():
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.token
}
}
So state.auth.token must be undefined also. That's as far as I can get from what you have shown me. You will need to debug further to figure out why. You can use the React Dev Tools to inspect props of your components. You can use Redux Dev Tools to inspect and manipulate the redux state. Check what the value of auth.token is in state. Look where it is supposed to be set and find out why it isn't getting set to a valid value.
Be sure to check this article for tips on how to debug your code.
I'm trying to fetch data from firebase, then update the state of the app with the results and display the data as a list in a list component.
Everything works except the final list component displays it and immediately becomes blank again. After debugging, I found out it doesn't manage to correctly map the state to the props but I couldn't figure out how to achieve this. Thanks in advance
PlantList.js
import React, { Component } from 'react';
import PlantSummary from './PlantSummary';
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import { fetchMyPlants } from '../../store/actions/myPlantsActions'
var i =0;
class PlantList extends Component {
constructor(props) {
super(props);
this.state = { myPlants: []} ;
}
componentDidMount() {
console.log("componentDidMount() triggered & state",i,this.state);
console.log("componentDidMount() triggered & props ",i,this.props);
this.props.dispatch(fetchMyPlants());
}
render(){
i = i +1;
console.log("render()"+i,this.props,this.state);
const { myPlants } = this.props;
return(
<div className="plant-list section">
{myPlants && myPlants.map((plant) => {
return (
<Link to={'/plant/'+ plant.id}>
<PlantSummary plant={plant} key={plant.id} />
</Link>
)
})}
</div>
)
}
}
const mapStateToProps = (state) => {
console.log("mapStateToProps triggered",state);
return {
myPlants: state.myPlants.items
}
}
export default connect(mapStateToProps)(PlantList)
myPlantActions.js
export const FETCH_MY_PLANTS_BEGIN = 'FETCH_MY_PLANTS_BEGIN';
export const FETCH_MY_PLANTS_SUCCESS = 'FETCH_MY_PLANTS_SUCCESS';
export const FETCH_MY_PLANTS_FAILURE = 'FETCH_MY_PLANTS_FAILURE';
export const fetchMyPlantsBegin = () => ({
type: FETCH_MY_PLANTS_BEGIN
});
export const fetchMyPlantsSuccess = myPlants => ({
type: FETCH_MY_PLANTS_SUCCESS,
payload: { myPlants }
})
export const fetchMyPlantsFailure = err => ({
type: FETCH_MY_PLANTS_FAILURE,
payload: { err }
});
export const fetchMyPlants = () => {
return(dispatch, getState, { getFirestore }) => {
dispatch(fetchMyPlantsBegin());
const firestore = getFirestore();
const authID = getState().firebase.auth.uid;
const usersPlants = [];
firestore.collection('users').doc(authID).collection('myPlants').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
firestore.collection('plants').doc(doc.data().id).get().then(
function(document) {
if (document.exists) {
const docToPushId = {id: doc.data().id};
let docToPush = {
...docToPushId,
...document.data()
};
usersPlants.push(docToPush);
} else {
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
}
);
});
}).then(myPlants => {
console.log("Dispatch happens now:",usersPlants);
dispatch(fetchMyPlantsSuccess(usersPlants));
return myPlants;
}).catch(error => dispatch(fetchMyPlantsFailure(error)));
}
};
myPlantsReducer.js
import {
FETCH_MY_PLANTS_BEGIN,
FETCH_MY_PLANTS_SUCCESS,
FETCH_MY_PLANTS_FAILURE
} from '../actions/myPlantsActions';
const initialState = {
items: [],
loading: false,
error: null
};
export default function myPlantsReducer(state = initialState, action) {
switch(action.type) {
case 'FETCH_MY_PLANTS_BEGIN':
return {
...state,
loading: true,
error: null
};
case 'FETCH_MY_PLANTS_SUCCESS':
return {
...state,
loading: false,
items: action.payload.myPlants
};
case 'FETCH_MY_PLANTS_FAILURE':
return {
...state,
loading: false,
error: action.payload.error,
items: []
};
default:
return state;
}
}
console logs
Not able to call function inside a component, provided by Redux.
This is my Container component
export class HomeContainer extends React.Component {
render() {
return(
<Home
getData={ this.props.getData }
/>
)
}
}
const mapStateToProps = state => {
return state;
}
const mapDispatchToProps = dispatch => {
return bindActionCreators({
getData: getCountriesList.getCountriesByISO
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeContainer);
I am calling that redux function inside the component using
componentDidMount() {
this.props.getData();
}
This is my Action
export function getCountriesByISOData() {
return (dispatch) => {
dispatch(toggleLoading(true));
return fetch('/api/countries', {
method: 'GET',
}).then(res => {
if(res.status === 200) {
return res.json().then(res=> {
console.log(res)
dispatch(toggleLoading(false));
dispatch(isSuccess(res));
})
} else {
dispatch(toggleLoading(false));
dispatch(isError(res.statusText));
}
}).catch(error => {
dispatch(toggleLoading(false));
dispatch(isError(error));
})
}
}
and this is my reducer
let defaultState = {
isLoading: false,
data: [],
error: ''
}
const getCountriesByISOReducer = (state=defaultState, action) => {
if(action.type === 'COUNTRIES_ISO_TOGGLE_LOADING') {
return {
...state,
isLoading: action.isLoading
}
}
if(action.type === 'COUNTRIES_ISO_IS_ERROR') {
return {
...state,
error: action.error
}
}
if(action.type === 'COUNTRIES_ISO_IS_SUCCESS') {
return {
...state,
data: [...state.data, action.success]
}
}
return state;
}
export default getCountriesByISOReducer;
This is my root reducer
export default combineReducers({
globalConfirmedCases: globalConfirmedCasesReducer,
dailyStatsByDate: getDailyStatsByDateReducer,
dailyStats: getDailyStatsReducer,
getCountriesByISO: getCountriesByISOReducer,
getCountriesStats: getCountriesStatsReducer
});
What I am doing wrong here? Is there any naming conventions while writing reducers and actions and using it in the react context?
I am using multiple reducers in my project and then combining them with combineReducers() function and have all actions in single file. when i dispatch the action, it is returning me state values to undefined. I think It can't find out because of multiple reducerse. But when i use single reducer file. It is working fine. Can anyone please tell me what the issue.It is how i am combining the reducers.
const rootReducer = combineReducers({
isMobileReducer,
imageSliderReducer
})
and now passing to store, like below:
let store = createStore(rootReducer,applyMiddleware(thunk))
and in frontend how i am accessing state
const mapStateToProps = (state) => ({
images: state.images,
isMobile: state && state.isMobile
})
imageSliderReducer.js
import {
FETCH_IMAGES_BEGIN,
FETCH_IMAGES_SUCCESS,
FETCH_IMAGES_FAILURE
} from '../actions/actionTypes'
const initialState = {
images:[],
error:null
}
const imageSliderReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_IMAGES_BEGIN:
return {...state,error:null}
case FETCH_IMAGES_SUCCESS:
return {...state,images:action.payload.images}
case FETCH_IMAGES_FAILURE:
return {...state,error:action.payload.error,images:[]}
default:
return state
}
}
export default imageSliderReducer;
isMobileReducer.js
import {
OPEN_MENU,
CLOSE_MENU,
SET_DEVICE_TYPE,
} from '../actions/actionTypes'
const initialState = {
isMenuOpen: null,
isMobile: false
}
const isMobileReducer = (state = initialState, action) => {
switch (action.type) {
case OPEN_MENU:
return {...state, isMenuOpen: true}
case CLOSE_MENU:
return {...state, isMenuOpen: false}
case SET_DEVICE_TYPE:
return {...state, isMobile: action.isMobile}
default:
return state
}
}
export default isMobileReducer;
actionCreator.js
import {
OPEN_MENU,
CLOSE_MENU,
SET_DEVICE_TYPE,
FETCH_IMAGES_BEGIN,
FETCH_IMAGES_SUCCESS,
FETCH_IMAGES_FAILURE
} from './actionTypes'
export function openMenu(isMobile) {
return {
type: OPEN_MENU
}
}
export function closeMenu(isMobile) {
return {
type: CLOSE_MENU
}
}
export function setDeviceType (isMobile) {
return {
type: SET_DEVICE_TYPE,
isMobile: isMobile
}
}
export function fetchImages() {
return dispatch => {
dispatch(fetchImagesBegin());
return fetch("https://7344.rio.com/wp-json/customapi/homeslider")
.then(handleErrors)
.then(res => res.json())
.then(json => {
dispatch(fetchImagesSuccess(json.posts));
return json.posts;
})
.catch(error => dispatch(fetchImagesFailure(error)));
};
}
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const fetchImagesBegin = () => ({
type: FETCH_IMAGES_BEGIN
});
export const fetchImagesSuccess = images => ({
type: FETCH_IMAGES_SUCCESS,
payload: { images }
});
export const fetchImagesFailure = error => ({
type: FETCH_IMAGES_FAILURE,
payload: { error }
});
Try using this:
const mapStateToProps = (state) => ({
images: state.imageSliderReducer.images,
isMobile: state.isMobileReducer.isMobile
})
I'm using this package https://github.com/RealScout/redux-infinite-scroll to make infinite scroll on list of brand. Here is my code:
Container:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actions, getBrands } from '../reducer';
import Infinite from 'react-infinite';
import InfiniteScroll from 'redux-infinite-scroll';
import SearchBox from '../components/SearchBox';
import CardList from '../components/CardList';
const { fetchBrands } = actions;
class BrandList extends Component {
componentDidMount() {
this.props.fetchBrands({ page: 1 });
}
renderList() {
const brands = this.props.brands;
return brands.map((brand) => {
return (
<CardList key={brand.id} name={brand.name} avatar={brand.avatar.thumbnail} follower={brand.follows_count} />
);
});
}
toggle() {
return this.props.isFetching;
}
loadMore() {
const {lastPage, currentPage} = this.props;
const nextPage = currentPage ? parseInt(currentPage) + 1 : 1;
if(currentPage && currentPage <= lastPage){
this.props.fetchBrands({page: nextPage});
}
}
render() {
return (
<div>
<SearchBox />
<div className="row">
<InfiniteScroll
items={this.renderList()}
loadMore={this.loadMore.bind(this)}
/>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
brands: getBrands(state),
isFetching: state.brand.isFetching,
currentPage: state.brand.currentPage,
lastPage: state.brand.lastPage
};
}
export default connect(mapStateToProps, { fetchBrands })(BrandList);
Reducer:
import axios from 'axios';
// Define Types
export const types = {
// brand list
FETCH_BRANDS: 'fetch_brands',
FETCH_BRANDS_SUCCESS: 'fetch_brands_success',
FETCH_BRANDS_ERROR: 'fetch_brands_failure',
FETCH_BRAND: 'fetch_brand',
FETCH_BRAND_SUCCESS: 'fetch_brand_success',
FETCH_BRAND_ERROR: 'fetch_brand_failure',
};
const { FETCH_BRANDS, FETCH_BRANDS_SUCCESS, FETCH_BRANDS_ERROR } = types;
// Define Reducer
export const INITIAL_STATE = { brands: [], brand: {}, isFetching: false, error: null, currentPage: 1 };
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case FETCH_BRANDS:
return { ...state, isFetching: true };
case FETCH_BRANDS_SUCCESS:
return { ...state, brands: action.payload.brands.data, currentPage: action.payload.brands.current_page, lastPage: action.payload.brands.last_page };
case FETCH_BRANDS_ERROR:
return { ...state, error: action.payload };
default:
return state;
}
}
// Define Actions
export const actions = {
fetchBrands: ({page, count = 15}) => {
return (dispatch) => {
dispatch({ type: FETCH_BRANDS });
axios.get(`brands?page=${page}&count=${count}`)
.then((response) => {
const {data} = response;
if (data.code == 200) {
dispatch({ type: FETCH_BRANDS_SUCCESS, payload: data });
}
});
};
}
};
// SELECTOR
export const getBrands = (state) => state.brand.brands;
it run loadMore function successfully but it not extend current list, it replace it instead.
loadmore function only run once. it should run 10 times.
do I miss something on my code to make it scroll?
Try adding
brands: [ ...state.brands, ...action.payload.brands.data]
like this in your reducer
case FETCH_BRANDS_SUCCESS:
return { ...state, brands: [ ...state.brands, ...action.payload.brands.data], currentPage: action.payload.brands.current_page, lastPage: action.payload.brands.last_page };
Which means that you are concating current list with upcoming list (versioned data)