Weird Behaviour Redux - reactjs

In my component I want to check when the parameter has changed and update accordingly. However when I do this, I am seeing weird behaviour and multiple requests been made to my api.
my component:
componentWillMount() {
this.state = {
path: this.props.match.params.categoryName,
};
}
componentDidUpdate(prevProps) {
if (prevProps === undefined) {
return false;
}
if (this.state.path !== this.props.match.params.categoryName) {
this.getCategory()
}
}
getCategory() {
if (this.props.allPosts && this.props.allPosts.length) {
const categoryId = _.result(_.find(this.props.allPosts, v => (
v.name === this.props.match.params.categoryName ? v.id : null
)), 'id');
this.props.dispatch(Actions.fetchCategory(categoryId));
}
}
my action:
import Request from 'superagent';
import Constants from '../constants';
const Actions = {
fetchCategory: categoryId => (dispatch) => {
dispatch({ type: Constants.CATEGORY_FETCHING });
Request.get(`/api/v1/categories/${categoryId}`)
.then((data) => {
dispatch({
type: Constants.CATEGORY_RECEIVED,
category: { id: data.body.id, name: data.body.name },
category_posts: data.body.posts,
});
});
},
};
export default Actions;
my reducer:
import Constants from '../constants';
const initialState = {
posts: [],
category: [],
fetching: true,
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case Constants.CATEGORY_FETCHING:
return Object.assign({}, state, { fetching: true });
case Constants.CATEGORY_RECEIVED:
return Object.assign({}, state, { category: action.category,
posts: action.category_posts,
fetching: false });
default:
return state;
}
}

Related

Setting data in React for useContext

could you please help with setting state in useContext ?
I am trying to send video variable through useEffect to setMediaContent to update mediaContext.media object. My goal is to have several media(video,images,posters) objects in media object, e.g.
https://codesandbox.io/s/friendly-sunset-o67nvj?file=/src/context.js
Thanks in advance
Try using a reducer:
import { createContext, useReducer } from "react";
// default state
const contextDefaultValues = {
video: { url: "", title: "", shown: false },
openVideo: () => {},
closeVideo: () => {},
mediaContent: { media: {}, title: "most" },
setMediaContent: () => {},
};
const MainReducer = (state = contextDefaultValues, action) => {
const { type, payload } = action;
switch (type) {
case "setMediaContent": {
const { media, title } = payload;
return { ...state, media: { ...state.media, ...media }, title: title };
}
case "closeVideo": {
return { ...state, shown: false };
}
case "openVideo": {
const { url, title } = payload;
return { ...state, url, title, shown: true };
}
default: {
throw new Error(`Unhandled action type: ${type}`);
}
}
};
export const MainContext = createContext(contextDefaultValues);
// provider recuder
const MainProvider = ({ children }) => {
const [state, dispatch] = useReducer(MainReducer, contextDefaultValues);
const openVideo = (url, title) => {
dispatch({ type: "openVideo", payload: { url, title, shown: true } });
};
const closeVideo = () => {
dispatch({ type: "closeVideo", payload: { shown: false } });
};
const setMediaContent = (media, title) => {
dispatch({ type: "setMediaContent", payload: { media, title } });
};
return (
<MainContext.Provider
value={{ ...state, setMediaContent, closeVideo, openVideo }}
>
{children}
</MainContext.Provider>
);
};
export default MainProvider;
Based on the provided sandbox, You have the render of the provider wrapped in the setMediaContent function.
Look at the { and } at line 36 and 58.
Code screenshot with misplaced brackets

Why i can not pass response data to redux store

I am having trouble to dispatch component state value to redux store .My idea is that i will dispatch to redux store only API response data and after can access it from different screens:
When i try it , i am getting error: ReferenceError: action is not defined
at loginSuccessful (http://packager.yz-9ze.anonymous.easy…}
Here is my component code:
import { loginSuccessful } from "../../redux/actions/loginacction";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
user: {
login: "",
password: ""
},
activity: false,
response: {},
showpassword: true
};
}
_login = async () => {
if (this.state.user.login !== "" && this.state.user.password !== "") {
console.log(this.state);
this.setState({ activity: true });
try {
await Axios.post(LoginAPI, this.state, {
headers: { appversion: 1.4 }
})
.then(response => {
console.log(response);
const status = response.status;
if (status === 200) {
this.setState({ activity: false });
this.setState({ response: response.data });
const userData = this.state.response;
this.props.dispatch(loginSuccessful(userData));
//this.props.login_api_call;
//this.props.navigation.navigate("CreateMove");
console.log(this.state);
}
})
.catch(error => {
console.log({ error });
this.setState({ activity: false });
Alert.alert("Error", error.response.data.error);
});
} catch (error) {}
} else {
Alert.alert("Support", "Enter Email and Password");
}
};
const mapStateToProps = state => {
return {
userData: state.loginCredentialsReducer
};
};
// const mapDispatchToProps = dispatch => {
// return {
// login_api_call: userData => dispatch(loginSuccessful(userData))
// };
// };
export default connect(mapStateToProps)(Login);
Here is my action type and creator:
export const LOGINSUCCESSFUL = "LOGINSUCCESSFUL";
export const loginSuccessful = userData => {
console.log(action);
return {
type: LOGINSUCCESSFUL,
payload: userData
};
};
And here is my reducer
import {LOGINSUCCESSFUL} from "../actions/actiontypes";
const initialState = {
userData: {}
};
const loginCredentialsReducer = (state = initialState, action) => {
switch (action.type) {
case LOGINSUCCESSFUL:
return {
...state,
userData: action.payload
};
default:
return state;
}
};
export default loginCredentialsReducer;
You're calling console.log(action) here, but action isn't declared anywhere. Hence the error.
export const LOGINSUCCESSFUL = "LOGINSUCCESSFUL";
export const loginSuccessful = userData => {
console.log(action); // <-- action is not declared anywhere
return {
type: LOGINSUCCESSFUL,
payload: userData
};
};
In this:
export const loginSuccessful = userData => {
console.log(action);
return {
type: LOGINSUCCESSFUL,
payload: userData
};
};
you never defined action hence the error.

Dispatching action calls incorrect reducer

Summary
In order to learn Redux, I am incorporating some state, actions, reducers, and trying to see how they are used in React Components.
I have set up a test object...
const initialState = {
navigationCount : 0,
someNumber : 500,
someList : ['aa',22,'c5d6','45615'],
};
...and aim to:
increment the navigationCount by 1 when visiting pages
add or subtract from someNumber
push() & pop() elements from someList.
Versions
Currently using gatsby ^2.5.0, react ^16.8.6, and react-redux ^6.0.1.
Code
actions & reducers
import { combineReducers } from 'redux';
import {
PAGE_INCREMENT,
NUMBER_INCREASE,
NUMBER_DECREASE,
LIST_PUSH,
LIST_POP,
} from './actionTypes.js';
// state
const initialState = {
navigationCount : 0,
someNumber : 500,
someList : ['aa',22,'c5d6','45615'],
};
// action creators returning actions
export const pageIncrementer = navigationCount => {
return {
type: PAGE_INCREMENT,
navigationCount,
};
};
export const numberAdder = numberToAdd => {
return {
type: NUMBER_INCREASE,
numberToAdd,
};
};
export const numberMinuser = numberToMinus => {
return {
type: NUMBER_DECREASE,
numberToMinus,
};
};
export const listPusher = itemToAdd => {
return {
type: LIST_PUSH,
itemToAdd,
}
};
export const listPopper = () => {
return {
type: LIST_POP,
}
};
// reducers
const pageIncrementReducer = (state = initialState, action) => {
switch (action.type) {
case PAGE_INCREMENT:
return Object.assign({}, ...state, {
navigationCount: action.navigationCount+1
});
default:
return state.navigationCount;
}
};
const numberChanger = (state = initialState, action) => {
switch (action.type) {
case NUMBER_INCREASE:
return Object.assign({}, ...state, {
someNumber: state.someNumber+action.numberToAdd,
});
case NUMBER_DECREASE:
return Object.assign({}, ...state, {
someNumber: state.someNumber-action.numberToMinus,
});
default:
return state.someNumber;
};
};
const listChanger = (state = initialState, action) => {
switch (action.type) {
case LIST_POP:
return Object.assign({}, ...state, {
someList: state.someList.pop(),
});
case LIST_PUSH:
return Object.assign({}, ...state, {
someList: state.someList.push(action.itemToAdd),
});
default:
return state.someList;
}
}
// store
const rootReducer = combineReducers({
pageIncrementReducer,
numberChanger,
listChanger,
});
export default rootReducer;
React Component
import React from 'react';
import Layout from '../components/common/Layout.jsx';
import LandingBanner from '../components/landing/LandingBanner.jsx';
import LandingNavgrid from '../components/landing/LandingNavgrid.jsx';
import LandingApp from '../components/landing/LandingApp.jsx';
import { connect } from 'react-redux';
import {
PAGE_INCREMENT,
NUMBER_INCREASE,
NUMBER_DECREASE,
LIST_PUSH,
LIST_POP,
} from '../state/actionTypes';
class LandingPage extends React.Component {
constructor(props){
super(props);
this.state = {
appliedNum: 2000,
};
}
componentDidMount(){
// this.props.pageIncrement(); // => numberChanger returned undefined
// this.props.numberIncrease(4444); // => pageIncrementReducer returned undefined
// this.props.numberDecrease(4444); // => pageIncrementReducer returned undefined
// this.props.listPush(4444); // => pageIncrementReducer returned undefined
this.props.listPop();
}
render(){
return (
<Layout>
<LandingBanner/>
<LandingNavgrid/>
<LandingApp/>
</Layout>
)
}
}
const filterNumbers = (list=[]) => {
console.log('filterNumbers list: ', list);
return list.filter(listElement => !!Number(listElement));
};
const mapStateToProps = (state, ownProps) => {
return {
someNumber: state.someNumber,
someList: filterNumbers(state.someList),
navigationCount: state.navigationCount,
};
};
const mapDispatchToProps = (dispatch) => {
return {
pageIncrement: () => dispatch({ type: PAGE_INCREMENT }),
numberIncrease: () => dispatch({ type: NUMBER_INCREASE }),
numberDecrease: () => dispatch({ type: NUMBER_DECREASE }),
listPush: () => dispatch({ type: LIST_PUSH }),
listPop: () => dispatch({ type: LIST_POP }),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(LandingPage);
Errors
redux.js:449 Uncaught Error: Given action "LIST_POP", reducer
"pageIncrementReducer" returned undefined. To ignore an action, you
must explicitly return the previous state. If you want this reducer to
hold no value, you can return null instead of undefined.
first of all, you always need to return state on the default switch case.
default:
return state;

Respond to a Single Redux Action in Multiple Reducers redux

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
})

Error in sticking actions to component react-redux

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?

Resources