I have normalized by data in redux, but now grabbing and display it on the front end is causing issues.
Ive been trying to fix this for a few days, i've tried what i feel is the most obvious this.props.... but i'm clearly missing something. Was using Manning newest book on Redux, Redux in Action, which has been a great guide for me so far.
I was trying to break up the data, so the tree would have business (dashboard) and business_locations (another page).
in my Redux DevTools console, the Action Tree looks like:
type: 'RECEIVE_ENTITIES'
payload
entities
business_locations
- 652 {id: 652, business_id: 452...}
business
- 452 {id: 452}
result: 452
my businessReducer:
import {
RECEIVE_ENTITIES,
FETCH_BUSINESS_STARTED,
FETCH_BUSINESS_SUCCESS,
SET_CURRENT_BUSINESS_ID,
} from '../../actions/types';
const initialBusinessState = {
items: [],
isLoading: false,
error: null,
};
export const getBusinesses = state => {
return Object.keys(state.business.items).map(id => {
return state.business.items[id];
});
};
export default function businesses(state = initialBusinessState, action) {
switch (action.type) {
case RECEIVE_ENTITIES: {
const { entities } = action.payload;
if (entities && entities.businesses) {
return {
...state,
isLoading: false,
items: entities.businesses,
};
}
return state;
}
case FETCH_BUSINESS_STARTED: {
return {
...state,
isLoading: true,
};
}
case FETCH_BUSINESS_SUCCESS: {
return {
...state,
isLoading: true,
items: action.payload.businesses,
};
}
default: {
return state;
}
}
}
export function setCurrentBusinessId(id) {
return {
type: SET_CURRENT_BUSINESS_ID,
payload: {
id,
},
};
}
my businessActions;
import { normalize } from 'normalizr';
import {
FETCH_BUSINESS_STARTED,
FETCH_BUSINESS_ERROR,
RECEIVE_ENTITIES,
SET_CURRENT_BUSINESS_ID,
} from './../types';
import * as schema from '../schema';
import * as api from '../../api';
function fetchBusinessStarted() {
return {
type: FETCH_BUSINESS_STARTED,
};
}
function fetchBusinessFailed(err) {
return {
type: FETCH_BUSINESS_ERROR,
payload: {
err,
},
};
}
function receiveEntities(entities) {
return {
type: RECEIVE_ENTITIES,
payload: entities,
};
}
export function setCurrentBusinessId(id) {
return {
type: SET_CURRENT_BUSINESS_ID,
payload: {
id,
},
};
}
export function fetchBusinesses() {
return (dispatch, getState) => {
dispatch(fetchBusinessStarted());
return api
.fetchBusinesses()
.then(resp => {
const businesses = resp.data;
const normalizedData = normalize(businesses, schema.businessSchema);
console.log('normalizedData', normalizedData);
dispatch(receiveEntities(normalizedData));
if (!getState().page.currentBusinessId) {
const defaultBusinessId = sessionStorage.getItem('business_id');
dispatch(setCurrentBusinessId(defaultBusinessId));
}
})
.catch(err => {
console.log(err);
fetchBusinessFailed(err);
});
};
}
my pageReducer
import { SET_CURRENT_BUSINESS_ID } from '../../actions/types';
const InitialPageState = {
currentBusinessId: null,
};
export default function page(state = InitialPageState, action) {
switch (action.type) {
case SET_CURRENT_BUSINESS_ID: {
return {
...state,
currentBusinessId: action.payload.id,
};
}
default: {
return state;
}
}
}
my schema
import { schema } from 'normalizr';
const businessLocationSchema = new schema.Entity('business_locations');
const businessSchema = new schema.Entity('business', {
business_locations: [businessLocationSchema],
});
export { businessSchema };
rootReducer
const rootReducer = (state = {}, action) => {
return {
page: pageReducer(state.page, action),
business: businessReducer(state.businesses, action),
businessLocation: businessLocationsReducer(state.businessLocations,
action),
form: formReducer,
};
};
front end:
class Dashboard extends React.Component {
componentDidMount() {
this.props.dispatch(fetchBusinesses());
}
render() {
if (this.props.isLoading) {
return (
<SpinContainer>
<Spin size="large" />
</SpinContainer>
);
}
console.log(this.props);
return (
<Wrapper>
<Card bordered={false}>
<Maps
zoom={16}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `30vh` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
<p>{this.props.business.website}</p>
<p>{this.props.business.businessName}</p>
</Card>
<Link to="/locations">
<Card bordered={false}>
<h1>****</h1>
</Card>
</Link>
<Link to="stamp-cards">
<Card bordered={false}>
<h1>*****</h1>
</Card>
</Link>
<Card bordered={false}>
<h1>*****</h1>
</Card>
</Wrapper>
);
}
}
function mapStateToProps(state) {
const { isLoading, business } = state.business;
return {
business: getBusinesses(state),
isLoading,
};
}
export default connect(mapStateToProps)(Dashboard);
Related
I am using MUI Snackbar in my root component (using STORE) and it appeared more than once because when store updated, the component gets re-rendered again
Snackbar Component:
export type BaseLayoutProps = {
children: ReactNode;
};
const Toast: FC<BaseLayoutProps> = ({ children }) => {
const [state,] = useSetupsStore();
const { toastProps } = state;
const [open, setOpen] = useState(toastProps?.toastState);
useEffect(() => {
if (toastProps) {
setOpen(toastProps.toastState);
}
}, [toastProps]);
const handleClose = (
event: React.SyntheticEvent | Event,
reason?: string
) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
return (
<>
<Snackbar
theme={unityTheme}
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
autoHideDuration={
toastProps?.toastLength ? toastProps.toastLength : 5000
}
variant={toastProps?.variant}
open={open}
onClose={handleClose as any}
message={toastProps?.toastMessage ? toastProps?.toastMessage : "Testing"}
TransitionComponent={(props) => <Slide {...props} direction="up" />}
/>
{children}
</>
)
}
export default Toast;
SnackBar Action:
export const setToast = (toastProps: IToast): StoreActionType => {
if (toastProps?.toastState) {
return { type: ActionTypes.SET_TOAST, payload: toastProps };
}
};
Snackbar Reducer
export const ToastReducer = (
state: IToast,
action: StoreActionType
): IToast => {
switch (action.type) {
case ActionTypes.SET_TOAST:
return {
...state,
toastState: (action.payload as IToast).toastState,
variant: (action.payload as IToast).variant,
toastMessage: (action.payload as IToast).toastMessage,
// toastLength: (action.payload as IToast).toastLength,
}
default:
return state
}
};
Dispatch Code:
dispatch(
setToast({
toastMessage: ToastMessages.Record_Added_Success,
toastState: true,
})
);
I'm 100% sure that it is dispatching only once but when another api get called(store get updated) it again comes to my reducer and root component get re-rendered again due to which it appeared twice. I have also return null in default case in reducer like this:
export const ToastReducer = (
state: IToast,
action: StoreActionType
): IToast => {
switch (action.type) {
case ActionTypes.SET_TOAST:
console.log("inside case",action);
return {
...state,
toastState: (action.payload as IToast).toastState,
variant: (action.payload as IToast).variant,
toastMessage: (action.payload as IToast).toastMessage,
}
default:
return null
}
In above case Snackbar appeared only once for a milisecond & then disappeared again
How to resolve this?
I have an issue with my React application (with Redux Saga), I'm getting the console error:
The service worker navigation preload request was cancelled before 'preloadResponse' settled. If you intend to use 'preloadResponse', use waitUntil() or respondWith() to wait for the promise to settle.
I see this error on the console only on Chrome, not in Firefox or Edge.
This error does not affect my application.
The following steps reproduce the error:
1. Main page upload.
2. Go to movie details page.
3. Go back to main page.
Main.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { mainActions } from '../../store/actions/actions';
import './Main.scss';
import { MoviesList, SearchPanel } from '../../components';
const propTypes = {};
const defaultProps = {};
class Main extends Component {
constructor(props) {
super(props);
this.handleSearchTextChange = this.handleSearchTextChange.bind(this);
this.handleLoadMoreButtonClick = this.handleLoadMoreButtonClick.bind(this);
this.handleMovieClick = this.handleMovieClick.bind(this);
this.handleFavoriteMovieClick = this.handleFavoriteMovieClick.bind(this);
}
componentDidMount() {
this.handleComponentDidMount();
}
handleComponentDidMount() {
const { moviesList } = this.props;
if (!moviesList || moviesList.length <= 0) {
this.getMovies(null, false);
}
}
handleLoadMoreButtonClick() {
this.getMovies(null, false);
}
handleMovieClick(e) {
if (e.target.className === 'movie') {
this.props.history.push(`/details/${e.currentTarget.dataset.id}`);
}
}
handleSearchTextChange(e) {
const { pageNumber, favoriteMoviesList } = this.props;
this.props.onSearchTextChange({
searchText: e.target.value,
pageNumber: pageNumber,
favoriteMoviesList: favoriteMoviesList
});
}
handleFavoriteMovieClick(e) {
const { id, name, posterId } = e.currentTarget.dataset;
const { moviesList, favoriteMoviesList } = this.props;
this.props.onUpdateFavoriteMovies({
updatedMovie: { id: id, name: name, posterId: posterId },
favoriteMoviesList: favoriteMoviesList,
moviesList: moviesList
});
}
getMovies(updatedSearchText, isSearchChange) {
const { searchText, pageNumber, favoriteMoviesList } = this.props;
this.props.onLoadMovies({
pageNumber: pageNumber,
favoriteMoviesList: favoriteMoviesList,
updatedSearchText: isSearchChange ? updatedSearchText : searchText,
isSearchChange: isSearchChange
});
}
render() {
const { searchText, isLoadingMoreMovies, isPager, moviesList } = this.props;
return (
<div className="main-area">
<SearchPanel
searchText={searchText}
onSearchTextChange={this.handleSearchTextChange}
/>
<MoviesList
pageName='movies'
moviesList={moviesList}
isLoadingMoreMovies={isLoadingMoreMovies}
isPager={isPager}
onLoadMoreClick={this.handleLoadMoreButtonClick}
onMovieClick={this.handleMovieClick}
onFavoriteMovieClick={this.handleFavoriteMovieClick}
/>
</div>
);
}
}
Main.propTypes = propTypes;
Main.defaultProps = defaultProps;
const mapStateToProps = (state) => {
return {
searchText: state.main.searchText,
pageNumber: state.main.pageNumber,
isLoadingMoreMovies: state.main.isLoadingMoreMovies,
isPager: state.main.isPager,
moviesList: state.main.moviesList,
favoriteMoviesList: state.main.favoriteMoviesList
};
};
const mapDispatchToProps = (dispatch) => {
return {
onLoadMovies: (request) => dispatch(mainActions.loadMovies(request)),
onSearchTextChange: (request) => dispatch(mainActions.searchTextChange(request)),
onUpdateFavoriteMovies: (request) => dispatch(mainActions.updateFavoriteMovies(request))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);
Details.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { detailsActions, mainActions } from '../../store/actions/actions';
import './Details.scss';
import { ActorsList, ButtonClick, CrewsList, FeaturesList, PageTitle, ProductionsList, Rating, Trailer } from '../../components';
import movieUtils from '../../utils/movie.utils';
const propTypes = {};
const defaultProps = {};
class Details extends Component {
constructor(props) {
super(props);
this.handleBackClick = this.handleBackClick.bind(this);
this.handleFavoriteMovieClick = this.handleFavoriteMovieClick.bind(this);
this.isFavorite = false;
}
componentDidMount() {
this.handleComponentDidMount();
}
handleComponentDidMount() {
if (this.props.moviesList.length <= 0) {
this.handleBackClick();
return;
}
const movieId = this.props.match.params.id;
if (!movieId) {
this.handleBackClick();
return;
}
this.props.onLoadMovieDetails(movieId);
this.updateIsFavorite(movieId);
}
numberWithCommas(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
updateIsFavorite(movieId) {
this.isFavorite = this.props.favoriteMoviesList.findIndex(movie => parseInt(movie.id) === parseInt(movieId)) > -1;
}
handleBackClick() {
this.props.history.push(`/`);
}
handleFavoriteMovieClick() {
const { movie, moviesList, favoriteMoviesList } = this.props;
this.props.onUpdateFavoriteMovies({
updatedMovie: { id: movie.id, name: movie.title, posterId: movie.poster_path },
favoriteMoviesList: favoriteMoviesList,
moviesList: moviesList
});
this.updateIsFavorite(movie.id);
}
render() {
const { movie, youtubeKey, credits } = this.props;
if (!movie) {
return null;
}
const { adult, poster_path, budget, genres, homepage, imdb_id, original_language, original_title,
overview, popularity, production_companies, production_countries, release_date, revenue, runtime, spoken_languages,
status, tagline, title, video, vote_average, vote_count } = movie;
const genresText = genres.map(genre => genre.name).join(', ');
const countriesText = production_countries.map(country => country.name).join(', ');
const languagesText = spoken_languages.map(language => language.name).join(', ');
const featuresList = [
{ item: 'Release Date', value: release_date },
{ item: 'Budget', value: `$${this.numberWithCommas(budget)}` },
{ item: 'Revenue', value: `$${this.numberWithCommas(revenue)}` },
{ item: 'Length', value: `${runtime} minutes` },
{ item: 'Popularity', value: popularity },
{ item: 'Original Title', value: original_title },
{ item: 'For Adults', value: adult ? 'Yes' : 'No' },
{ item: 'Original Language', value: original_language },
{ item: 'Spoken Languages', value: languagesText },
{ item: 'Countries', value: countriesText },
{ item: 'Status', value: status },
{ item: 'Is Video', value: video ? 'Yes' : 'No' }
];
const linksList = [];
if (homepage) {
linksList.push({ id: 1, name: 'Homepage', url: homepage });
}
if (imdb_id) {
linksList.push({ id: 2, name: 'IMDB', url: `https://www.imdb.com/title/${imdb_id}` });
}
const actorsList = movieUtils.removeDuplicates(credits ? credits.cast ? credits.cast : null : null, 'name');
const crewsList = movieUtils.removeDuplicates(credits ? credits.crew ? credits.crew : null : null, 'name');
return (
<div>
<section className="details-area">
<PageTitle
pageName='details'
pageTitle='Details'
/>
<ul className="details-content">
<li className="details-left" style={{ backgroundImage: `url('https://image.tmdb.org/t/p/original${poster_path}')` }}></li>
<li className="details-right">
<h2>{title} ({release_date.substr(0, 4)})</h2>
<p className="genres">{genresText}</p>
<p className="description short">{tagline}</p>
<Rating
rating={vote_average}
votesCount={this.numberWithCommas(vote_count)}
/>
<p className="description full">{overview}</p>
<div className="extra">
<FeaturesList
featuresList={featuresList.slice(0, 5)}
linksList={null}
isFavorite={this.isFavorite}
onFavoriteMovieClick={this.handleFavoriteMovieClick}
/>
{youtubeKey && <Trailer
youtubeKey={youtubeKey}
/>}
</div>
</li>
<div className="extra-features">
<FeaturesList
featuresList={featuresList.slice(5, featuresList.length)}
linksList={linksList}
isFavorite={null}
onFavoriteMovieClick={null}
/>
<ProductionsList
productionsList={production_companies}
/>
</div>
</ul>
</section>
<section className="actors-area">
<PageTitle
pageName='actors'
pageTitle='Cast'
/>
<ActorsList
actorsList={actorsList}
/>
</section>
<section className="crew-area">
<PageTitle
pageName='crew'
pageTitle='Crew'
/>
<CrewsList
crewsList={crewsList}
/>
</section>
<ButtonClick
buttonText={'Back'}
buttonTitle={'Back'}
isLoading={false}
onClick={this.handleBackClick}
/>
</div>
);
}
}
Details.propTypes = propTypes;
Details.defaultProps = defaultProps;
const mapStateToProps = (state) => {
return {
movie: state.details.movie,
youtubeKey: state.details.youtubeKey,
credits: state.details.credits,
moviesList: state.main.moviesList,
favoriteMoviesList: state.main.favoriteMoviesList
};
};
const mapDispatchToProps = (dispatch) => {
return {
onLoadMovieDetails: (movieId) => dispatch(detailsActions.loadDetails(movieId)),
onUpdateFavoriteMovies: (request) => dispatch(mainActions.updateFavoriteMovies(request))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Details);
What I already looked in:
Getting The service worker navigation preload request was cancelled before 'preloadResponse' settled
https://learn.microsoft.com/en-us/answers/questions/108004/getting-the-service-worker-navigation-preload-requ.html
https://support.google.com/mail/thread/4055804?hl=en
https://love2dev.com/pwa/service-worker-preload/
I tried to put this on Details.jsx page, but it didn't work:
self.addEventListener('fetch', event => {
event.respondWith(async function () {
// Respond from the cache if we can
const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse; // Else, use the preloaded response, if it's there
const response = await event.preloadResponse;
if (response) return response; // Else try the network.
return fetch(event.request);
}());
});
self.addEventListener('activate', event => {
event.waitUntil(async function () {
// Feature-detect
if (self.registration.navigationPreload) { // Enable navigation preloads!
console.log('Enable navigation preloads!');
await self.registration.navigationPreload.enable();
} return;
})();
});
How can I solve this issue? Thanks.
Had same error, even my iframe wasn't loading..whatever video you are using from youtube use nocookie/embed in url. It's working for me.
Try changing https://www.youtube.com/watch?v=i8eBBG46H8A to
https://www.youtube-nocookie.com/embed/i8eBBG46H8A
Hope nocookie & embed helps..!!
I have this function in my Effect functions to make an API call to the backend:
export function UpdateGroup(groupId: string, data: { name: string; desc: string; visibility: string, owners: string[], isApproved?: boolean, isRejected?: boolean, photo?: any }): Effect {
// console.log(data)
return async function (dispatch) {
try {
dispatch(loadingAction(true));
api
.update({
resource: `siteGroups`,
id: groupId,
data: {
name: data.name,
desc: data.desc,
visibility: data.visibility,
owners: data.owners,
isApproved: data.isApproved,
isRejected: data.isRejected
// photo: data.photo,
},
})
.then((res) => {
dispatch(FetchSiteGroup(groupId));
})
.catch((err) => new Error(err));
} catch (err) {
console.error(err);
dispatch(loadingAction(false));
}
};
}
This is what the api.update method looks like:
update(payload: { resource: string; id: string; data: any; query?: any }) {
return http.put(`${payload.resource}/${payload.id}`, payload.data, {
params: payload.query,
});
}
The Action Type:
export function fetchSiteGroupAction(group: ISiteGroup): ActionsType {
return {
type: ActionsEnum.FETCH_SITEGROUP,
payload: group,
};
}
The Reducer:
export function Reducer(state = initialState, action: ActionsType) {
switch (action.type) {
case ActionsEnum.LOADING:
return produce(state, (draft) => {
draft.loading = action.payload;
});
case ActionsEnum.AUTH_SUCCESS:
return produce(state, (draft) => {
draft.token = action.payload.token;
draft.user = action.payload.user;
draft.isAdmin =
action.payload.user.roles.find((r) => r === "admin") !== undefined;
draft.loading = false;
});
case ActionsEnum.LOGOUT:
return produce(state, (draft) => {
draft.token = "";
draft.loading = false;
});
case ActionsEnum.FETCH_MEMBERS:
return produce(state, (draft) => {
draft.members = action.payload;
draft.loading = false;
});
case ActionsEnum.FETCH_SITEGROUP:
return produce(state, (draft) => {
draft.siteGroup = action.payload;
// draft.loading = false;
});
default:
return state;
}
}
The function is called here when the Ok function is selected on the Modal card:
import { UpdateGroup } from "../store/Effects";
<Modal
title=""
visible={isModalVisible}
onOk={(e) => {
// e.preventDefault();
// ApproveGroup(groupInfo.groupId, { isApproved: true })
// console.log(groupInfo);
UpdateGroup(groupInfo.groupId, { name: groupInfo.displayName, desc: groupInfo.description, visibility: groupInfo.visibility, owners: groupInfo.owners, isApproved: true, isRejected: false })
// approveGroup(groupInfo.groupId, groupInfo)
handleCancel()
}}
onCancel={handleCancel}>
This group <b>({groupInfo.displayName})</b> will now be approved
</Modal> <Modal
title=""
visible={isRejectModalVisible}
onOk={(e) => {
// ApproveGroup(groupId, { isApproved: true })
e.preventDefault();
// console.log("Rejected");
UpdateGroup(groupInfo.groupId, { name: groupInfo.displayName, desc: groupInfo.description, visibility: groupInfo.visibility, owners: groupInfo.owners, isApproved: false, isRejected: true });
setIsRejectModalVisible(false);
}}
onCancel={handleRejectCancel}>
<p>This group <b style={{ color: "red" }}>({groupInfo.displayName})</b> will now be rejected
</p>
Comment: <Input.TextArea rows={4} />
</Modal>
The groupInfo content is gotten from a state which was updated from the
I'm going to go out on a limb and say you just haven't wrapped your UpdateGroup action creator in a call to dispatch. This can be done in a couple ways.
Using connect Higher Order Component
import { connect } from 'react-redux';
import { UpdateGroup } from "../store/Effects";
...
<Modal
title=""
visible={isModalVisible}
onOk={(e) => {
props.UpdateGroup(
groupInfo.groupId,
{
name: groupInfo.displayName,
desc: groupInfo.description,
visibility: groupInfo.visibility,
owners: groupInfo.owners,
isApproved: true,
isRejected: false,
},
);
handleCancel()
}}
onCancel={handleCancel}
>
...
const mapDispatchToProps = {
UpdateGroup,
};
export connect(null, mapDispatchToProps)(ComponentWithModal);
Using useDispatch react hook
import { useDispatch } from 'react-redux';
import { UpdateGroup } from "../store/Effects";
...
const dispatch = useDispatch();
<Modal
title=""
visible={isModalVisible}
onOk={(e) => {
dispatch(
UpdateGroup(
groupInfo.groupId,
{
name: groupInfo.displayName,
desc: groupInfo.description,
visibility: groupInfo.visibility,
owners: groupInfo.owners,
isApproved: true,
isRejected: false,
},
),
);
handleCancel();
}}
onCancel={handleCancel}
>
...
export ComponentWithModal;
I am trying to do a setting screen with two separate action functions. one for lb\kg and another for weight and calories, however if I change one of the functions the other gets hidden? or removed from screen.
Where I should be getting 150 lb, I can get either 150, or lb which are both created from separate actions. What am I doing wrong?
Where the reducer props get displayed.
<Text style={globalStyles.defaultText}>
Current Weight:{" "}
<Text>
{personalWeight} <--- be like 150
{weightProp} <---- be like lb
</Text>
{"\n"}
</Text>
actions page:
export const DISTANCE_SETTINGS = "DISTANCE_SETTINGS";
export const WEIGHT_SETTINGS = "WEIGHT_SETTINGS";
export const ALLINPUT_SETTINGS = "ALLINPUT_SETTINGS";
// settings button lists
export const settingsAction = (buttonGroupName, actionId) => dispatch => {
switch (buttonGroupName) {
case "distance":
dispatch({
type: DISTANCE_SETTINGS,
payload: actionId
});
break;
case "weight":
dispatch({
type: WEIGHT_SETTINGS,
payload: actionId
});
break;
default:
alert("There was an error somewhere");
}
};
// settings input options weight/calories
export const settingsInputs = data => dispatch => {
dispatch({
type: ALLINPUT_SETTINGS,
payload: data
});
};
reducers page:
import {
DISTANCE_SETTINGS,
WEIGHT_SETTINGS,
ALLINPUT_SETTINGS
} from "../actions/settingsAction";
export const inititalState = {
profile: {
weight: 150,
caloriesBurned: 100,
distanceSettings: "",
weightSettings: ""
}
};
export default function(state = inititalState, action) {
switch (action.type) {
case DISTANCE_SETTINGS:
return {
...state,
profile: {
distanceSettings: action.payload
}
};
case WEIGHT_SETTINGS:
let conversion = `${action.payload === "Pounds" ? "lb" : "kg"}`;
return {
...state,
profile: {
weightSettings: conversion
}
};
case ALLINPUT_SETTINGS:
return {
...state,
profile: {
weight: action.payload.weight,
caloriesBurned: action.payload.calories
}
};
default:
return state;
}
}
Your reducers should be:
export default function(state = inititalState, action) {
switch (action.type) {
case DISTANCE_SETTINGS:
return {
...state,
profile: {
...state.profile, // You don't have it
distanceSettings: action.payload
}
};
case WEIGHT_SETTINGS:
let conversion = `${action.payload === "Pounds" ? "lb" : "kg"}`;
return {
...state,
profile: {
...state.profile, // You don't have it
weightSettings: conversion
}
};
case ALLINPUT_SETTINGS:
return {
...state,
profile: {
...state.profile, // You don't have it
weight: action.payload.weight,
caloriesBurned: action.payload.calories
}
};
default:
return state;
}
}
I am trying to dispatch an action to update my state, I am using a picker in my component, I don't get any errors but state doesn't update. Any ideas?
onChange = e => {
setSelectedView(e);
};
const HomeScreen = () => {
return (
<View style={styles.container}>
<Image
source={require("../img/logoGreen.png")}
style={{ width: 150, height: 100 }}
/>
<View style={{ backgroundColor: "red" }}>
<Text>Please select a year</Text>
<Picker selectedValue={0} onValueChange={onChange}>
{studentYears.map((year, i) => {
return (
<Picker.Item
key={year.id}
label={year.title}
value={year.value}
/>
);
})}
</Picker>
</View>
</View>
);
};
const mapStateToProps = state => {
return {
selectedView: state.selectedView
};
};
const mapDispatchToProps = dispatch => {
return {
setSelectedView: e => dispatch(setSelectedView(e))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
Reducer:
export function setSelectedView(state = 0, action) {
switch (action.type) {
case "SET_SELECTED_VIEW":
return action.setSelectedView;
default:
return state;
}
}
Action:
export const setSelectedView = int => ({
type: "SET_SELECTED_VIEW",
selectedView: int
});
Store:
import {
eventsFetchData,
eventsHasErrored,
eventsIsLoading,
eventsFetchDataSuccess,
setSelectedView
} from "../actions/events";
import rootReducer from "../reducers";
const initialState = {
eventsHasErrored: false,
eventsIsLoading: true,
events: [],
setSelectedView: { selectedView: 0 }
};
const reduxLogger = createLogger();
const store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk, reduxLogger)
);
I think the problem is in your reducer
export function setSelectedView(state = 0, action) {
switch (action.type) {
case "SET_SELECTED_VIEW":
return action.selectedView;//instead of action.setSelectedView;
default:
return state;
}
}