I am working on a temperature control app with react native. To get the value of the temperature I use redux toolkit. My problem is that my code for increasing/decreasing the initial value with the reducers doesn't work. I get the 20 as value but using handlers to dispatch(in/decreaseTemp()) doesn't do anything. What am I doing wrong?
Reducers:
import { createSlice } from '#reduxjs/toolkit';
const initialStateValue = {
value: 20
}
const tempSlice = createSlice({
name: 'temp',
initialState: initialStateValue,
reducers: {
increaseTemp: (state = initialStateValue) => {
state = state + 1;
},
decreaseTemp: (state = initialStateValue) => {
state = state - 1;
}
}
});
export const {increaseTemp, decreaseTemp} = tempSlice.actions;
export default tempSlice.reducer;
The handlers:
function increaseTempHandler() {
dispatch(increaseTemp());
}
function decreaseTempHandler() {
dispatch(decreaseTemp());
}
The issue is a mismatch between how you've defined your state
const initialStateValue = {
value: 20
};
and how you're using it:
increaseTemp: (state = initialStateValue) => {
state = state + 1;
},
If state is an object that has a value property then your reducer should be returning a new state that has the updated value property.
e.g.
increaseTemp: (state = initialStateValue) => {
const { value } = state;
return { ...state, value: value+1 }
},
Related
The data is provided dynamically and I don't know its value to assign it in initialState. It causes me a bug that I can't deal with.
How to update the object in the state if there was no object in initialState?
ERROR
filteringSlice.ts:12
Uncaught TypeError: Cannot read properties of undefined (reading 'products')
at addFilter (filteringSlice.ts:12:1)
at createReducer.ts:280:1
at produce (immerClass.ts:94:1)
at createReducer.ts:279:1
at Array.reduce (<anonymous>)
at reducer (createReducer.ts:246:1)
at reducer (createSlice.ts:325:1)
at combination (redux.js:560:1)
at k (<anonymous>:2235:16)
at D (<anonymous>:2251:13)
CALL ACTION
onChange={(selectedValue) => {
dispatch(
addFilter({
products: { category__name: { filter: selectedValue } },
})
);
}}
SLICE
import { createSlice } from "#reduxjs/toolkit";
const initialState = {} as any;
const filteringSlice = createSlice({
name: "filtering",
initialState,
reducers: {
addFilter: (state, action) => {
const key = Object.keys(action.payload)[0];
const key2 = Object.keys(action.payload[key])[0];
const values = Object.values(action.payload[key])[0];
//#ts-ignore
state.filters[key][key2] = { ...state.filters[key][key2], ...values };
},
},
});
const { reducer, actions } = filteringSlice;
export const { addFilter } = actions;
export default reducer;
So your state is an empty object at first:
const initialState = {} as any;
But then you're accessing at as if it had a more deeply nested structure:
state.filters[key][key2] = ...
That doesn't work because there is no state['filters'], and no state['filters']['products'], etc.
You need to create every level of this nesting manually for this to work (or think about a better, flatter structure for your state):
/*
action.payload = {
products: {
category__name: {
filter: selectedValue
}
}
}
*/
const key = Object.keys(action.payload)[0]; // 'products'
const key2 = Object.keys(action.payload[key])[0]; // 'category__name'
const values = Object.values(action.payload[key])[0]; // selectedValue
if (!state.filters) {
state.filters = {};
}
if (!state.filters[key]) {
state.filters[key] = {};
}
if (!state.filters[key][key2]) {
state.filters[key][key2] = {};
}
state.filters[key][key2] = { ...state.filters[key][key2], ...values };
I was trying to implement a page-by-page onboarding signup screen for which the first page collects users horoscopic sign and in the next page, it asks for name. The thing is the sign_id gets replaced by name. Please check the codes below
action.js
import * as types from './types';
export function addNewUserRequest(values) {
console.log('action data', values);
return {
type: types.NEW_USER_REQ,
values,
};
}
reducer.js
import createReducer from '../lib/createReducer';
import * as types from '../actions/types';
const initialState = {
values: [],
};
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.values);
return {
...state,
values: action.values,
};
},
createreducer.js
export default function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
} else {
return state;
}
};
}
Page1.js
const dispatch = useDispatch();
const onPress = (val) => {
console.log('SELECTED SIGN', val);
let value = {
sign_id: val,
};
NavigationService.navigate('Login3');
dispatch(newUserActions.addNewUserRequest(value));
};
Page2.js
const dispatch = useDispatch();
const handlePress = () => {
let value = {
name: userName,
};
dispatch(newUserActions.addNewUserRequest(value));
NavigationService.navigate('Login4');
};
Console
Change param in addNewUserRequest from values to value as only single value is passed. Then append action.value to state.values.
export function addNewUserRequest(value) {
console.log('action data', value);
return {
type: types.NEW_USER_REQ,
value,
};
}
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.value);
return {
...state,
values: { ...state.values, ...action.value }
};
},
I have an ASP.NET Core project with React and Redux, I'm also using the Kendo React UI. I'm trying to return data to one of my Kendo widgets but I'm getting an error when I try to do so and I need help identifying what I've done wrong.
When I run my application I get the error of:
1 of 2 errors on the page TypeError: data.findIndex is not a function
DropDownList/_this.renderDropDownWrapper
C:/Users/Allan/node_modules/#progress/kendo-react-dropdowns/dist/es/DropDownList/DropDownList.js:83
80 | var focused = _this.state.focused; 81 | var opened =
_this.props.opened !== undefined ? _this.props.opened : _this.state.opened; 82 | var value = _this.value;
83 | var selectedIndex = data.findIndex(function (i) { return areSame(i, value, dataItemKey); }); 84 | var text =
getItemValue(value, textField); 85 | var valueDefaultRendering =
(React.createElement("span", { className: "k-input" }, text)); 86 |
var valueElement = valueRender !== undefined ?
In the console this error shows as:
Warning: Failed prop type: Invalid prop data of type string
supplied to DropDownList, expected array.
The error makes sense, but the data I'm returning should be an array. It's not though as it doesn't appear to return anything. So I've done something wrong.
Here is my code so far, please note that my data is served from a generic repository.
components/vessels/WidgetData.js
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { actionCreators } from '../../store/Types';
import { DropDownList } from '#progress/kendo-react-dropdowns';
class WidgetData extends Component {
state = {
vesseltypes: ""
};
componentWillMount() {
this.props.requestTypes();
}
render() {
return (
<div>
<DropDownList data={this.state.vesseltypes} />
</div>
);
}
}
export default connect(
state => state.vesseltypes,
dispatch => bindActionCreators(actionCreators, dispatch)
)(WidgetData);
components/store/Types.js
const requestVesselTypes = 'REQUEST_TYPES';
const receiveVesselTypes = 'RECEIVE_TYPES';
const initialState = {
vesseltypes: [],
isLoading: false
};
export const actionCreators = {
requestTypes: () => async (dispatch) => {
dispatch({ type: requestVesselTypes });
const url = 'api/KendoData/GetVesselTypes';
const response = await fetch(url);
const alltypes = await response.json();
dispatch({ type: receiveVesselTypes, alltypes });
}
}
export const reducer = (state, action) => {
state = state || initialState;
if (action.type === requestVesselTypes) {
return {
...state,
isLoading: true
};
}
if (action.type === receiveVesselTypes) {
alltypes = action.alltypes;
return {
...state,
vesseltypes: action.alltypes,
isLoading: false
}
}
return state;
};
And finally, the reducer is defined in the store
components/store/configureStore.js
const reducers = {
vesseltypes: Types.reducer
};
I've tested the API to ensure data is there and it works, I've logged said data to the console from Types.js in the store and I can see it's returned. I'm very much new to react with redux so I'm trying to find my way here and any help is appreciated.
You need to remove the following state definition, since you want to refer to the value in the redux store, not to a local value:
class WidgetData extends Component {
state = {
vesseltypes: ""
};
Then, in your code, you need to refer to the redux store value: this.props.vesseltypes:
class WidgetData extends Component {
state = {
vesseltypes: ""
};
componentWillMount() {
this.props.requestTypes();
}
render() {
return (
<div>
<DropDownList data={this.props.vesseltypes} />
</div>
);
}
}
And you need to change the connect definition:
export default connect(
vesseltypes => state.vesseltypes,
dispatch => bindActionCreators(actionCreators, dispatch)
)(WidgetData);
I have the following code
store/index.js
const DEFAULT_STATE = {
auth: { isAuthenticated: false },
error: { message: null },
tracks: [],
uploadedTrack: {}
};
store/reducers/index.js
import auth from './auth';
import error from './error';
import {tracks, uploadedTrack} from './tracks';
export default combineReducers({
auth,
tracks,
uploadedTrack,
error
});
store/reducers/tracks.js
import {UPLOADED_TRACK, SET_CURRENT_USER_TRACK} from '../actionTypes';
export const tracks = (state = [], action) => {
switch(action.type) {
case SET_CURRENT_USER_TRACK:
return action.tracks;
default:
return state;
}
}
export const uploadedTrack = (state = {}, action) => {
switch(action.type) {
case UPLOADED_TRACK:
return action.track;
default:
return state;
}
};
store/actions/tracks.js
export const setTrack = tracks => ({
type: SET_CURRENT_USER_TRACK,
tracks
});
export const setUploadedTrack = track => ({
type: UPLOADED_TRACK,
track
});
export const getUserTrack = () => {
return async dispatch => {
try {
const {token, ...tracks} = await api.call('get', 'tracks/user');
dispatch(setTrack(tracks));
dispatch(removeError());
} catch (err) {
const {error} = err.response.data;
dispatch(addError(error.message));
}
};
};
components/trackList.jsx
componentDidMount() {
const {getUserTrack} = this.props;
getUserTrack();
}
render() {
var {authType} = this.props;
const {auth} = this.props;
const {tracks} = this.props;
console.log("Track: ", tracks)
All seems works because my "tracks" on Redux store contains my list of six tracks, but when i try to print this information from the "tracks" variable on the console this print "undefined".
The strange things is that my "call" on the console contains my six tracks...
Can you help me?
I don't know where is my errors, i try to apply the solutions find on the web but nothing working.
Can you show how you map your redux state to your component ? If your redux store store contains your six tracks but you can't display them in your react component, the problem is probably how you bind your store to your component (When you call your connect() in your components/trackList.jsx).
If I had a component, which was loaded into a page, accepted a few props, made a couple of API calls and rendered a list, would they share the same redux store?
Say for example...
<Trending data-limit=5 data-offset=0 />
<div>Something here</div>
<Trending data-limit=5 data-offset-5 />
I have something similar to this and they seem to override each other.
If you mean React State, then no.
If you mean Redux Store State, by mapStateToProps or other way and your react component are connected to the same end point in the storeState then Yes
ex : let's say you have mapStateToPros linking the props of the component to this end point of the store State.App.User.Info.email
If email changes all component mapped to this end point will update
In the other hand if you're calling each component with it's own data, then each component lives in it's own space like the example you gave in your question
I put together an example to show how to use the same component with two different Redux containers that could be used to populate the store differently. I am actually confused now because the two reducers overwrite the same state, despite being separated by combineReducers.
Example:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import { createStore, combineReducers } from 'redux';
const ParentComponent = React.createClass({
propTypes: {
fetchData: React.PropTypes.func.isRequired,
data: React.PropTypes.string
},
componentDidMount: function () {
setTimeout(() => {
this.props.fetchData();
}, 2000);
},
render: function () {
return (
<div>{this.props.data}</div>
);
}
});
const ParentComponentContainer = React.createClass({
render: function () {
return (<ParentComponent {...this.props} />);
}
});
const mapStateToPropsFoo = (state) => {
if (state.exampleReducerFoo && state.exampleReducerFoo.data) {
return {
data: state.exampleReducerFoo.data
}
}
return {};
};
const mapStateToPropsBar = (state) => {
if (state.exampleReducerBar && state.exampleReducerBar.data) {
return {
data: state.exampleReducerBar.data
}
}
return {};
};
const mapDispatchToPropsFoo = (dispatch) => {
return {
fetchData: () => {
dispatch({
type: 'RECEIVE_DATA',
data: 'foo'
});
}
}
};
const mapDispatchToPropsBar = (dispatch) => {
return {
fetchData: () => {
dispatch({
type: 'RECEIVE_DATA',
data: 'bar'
});
}
}
};
const reducers = combineReducers({
exampleReducerFoo: (state = {}, action) => {
switch (action.type) {
case 'RECEIVE_DATA':
return Object.assign({}, state, {
data: action.data
});
default:
return state;
}
},
exampleReducerBar: (state = {}, action) => {
switch (action.type) {
case 'RECEIVE_DATA':
return Object.assign({}, state, {
data: action.data
});
default:
return state;
}
}
});
const store = createStore(reducers);
const ConnectedParentComponentContainerFoo = connect(mapStateToPropsFoo, mapDispatchToPropsFoo)(ParentComponentContainer);
const ConnectedParentComponentContainerBar = connect(mapStateToPropsBar, mapDispatchToPropsBar)(ParentComponentContainer);
ReactDOM.render(<Provider store={store}><div><ConnectedParentComponentContainerFoo data="aaa"/>something<ConnectedParentComponentContainerBar data="bbb"/></div></Provider>, document.getElementById('ReactApp'));
When the state gets to the mapStateToProps functions it's shape is:
{
exampleReducerBar: {
data: 'bar'
},
exampleReducerFoo: {
data: 'bar'
}
}
I expected the reducers to be writing to their own space in the state (reducerBar's data should be 'bar' and reducerFoo's data should be 'foo'), but apparently even though the reducers shape the state when using combineReducers, the state is shared between reducers. I am confused.