I have built this app using create-react-native-app, the action is dispatched but the state isn't being updated and I'm not sure why.
I see the action being logged (using middleware logger) but the store isn't getting updated, I am working on Add_Deck only for now
Here is my reducer:
// import
import { ADD_CARD, ADD_DECK } from './actions'
// reducer
export default function decks(state ={}, action){
switch(action.type){
case ADD_DECK:
return {
...state,
[action.newDeck.id]: action.newDeck
}
case ADD_CARD:
return {
...state,
[action.deckID]: {
...state[action.deckID],
cards: state[action.deckID].cards.concat([action.newCard])
}
}
default: return state
}
}
Actions file:
// action types
const ADD_DECK = "ADD_DECK";
const ADD_CARD = "ADD_CARD";
// generate ID function
function generateID() {
return (
"_" +
Math.random()
.toString(36)
.substr(2, 9)
);
}
// action creators
function addDeck(newDeck) {
return {
type: ADD_DECK,
newDeck
};
}
// export
export function handleAddDeck(title) {
return dispatch => {
const deckID = generateID();
// const newDeck = { id: deckID, title, cards: [] };
dispatch(addDeck({ id: deckID, title, cards: [] }));
};
}
function addCard(deckID, newCard) {
// { question, answer }, deckID
return {
type: ADD_CARD,
deckID,
newCard
};
}
// export
export function handleAddCard(deckID, content) {
// { question, answer }, deckID
return dispatch => {
const newCard = { [generateID()]: content };
dispatch(addCard(deckID, newCard));
};
}
And react-native component:
import React, { Component } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity } from "react-native";
import {red, white} from '../utils/colors'
import { connect } from 'react-redux'
import { handleAddDeck } from '../redux/actions'
class AddDeck extends Component {
state = {
text:""
}
handleSubmit = () => {
this.props.dispatch(handleAddDeck(this.state.text))
this.setState(()=>{
return { text: ""}
})
}
render() {
return (
<View style={styles.adddeck}>
<Text> This is add deck</Text>
<TextInput
label="Title"
style={{ height: 40, borderColor: "gray", borderWidth: 1 }}
onChangeText={text => this.setState({ text })}
placeholder="Deck Title"
value={this.state.text}
/>
<TouchableOpacity style={styles.submitButton} onPress={this.handleSubmit}>
<Text style={styles.submitButtonText}>Create Deck</Text>
</TouchableOpacity>
</View>
);
}
}
function mapStateToProps(decks){
console.log("state . decks", decks)
return {
decks
}
}
export default connect(mapStateToProps)(AddDeck);
const styles = StyleSheet.create({
adddeck: {
marginTop: 50,
flex: 1
},
submitButton: {
backgroundColor: red,
padding: 10,
margin: 15,
height: 40,
},
submitButtonText: {
color: white
}
});
I guess you forgot to export your types from the actions file thus the switch(action.type) does not trigger the needed case statement.
Maybe try to add as the following:
export const ADD_DECK = "ADD_DECK";
export const ADD_CARD = "ADD_CARD";
Or further debugging just to see if the values are the ones what you are looking for:
export default function decks(state = {}, action) {
console.log({type:action.type, ADD_DECK}); // see what values the app has
// further code ...
}
I hope that helps! If not, let me know so we can troubleshoot further.
Related
I am pretty sure am supplying a function to the LottieView component, but am getting the aforementioned console warning error: Failed prop type: Invalid prop onAnimationFinish of type object supplied to LottieView, expected a function., telling me I supplied an object. Here below is part of my code affiliated with the issue:
import React from "react";
import { View, StyleSheet, Modal } from "react-native";
import * as Progress from "react-native-progress";
import LottieView from "lottie-react-native";
import colors from "../config/colors";
function UploadScreen(onDone, progress = 0, visible = false) {
return (
<Modal visible={visible}>
<View style={styles.container}>
{progress < 1 ? (
<Progress.Bar
color={colors.primary}
progress={parseInt(progress)}
width={200}
/>
) : (
<LottieView
autoPlay
loop={false}
onAnimationFinish={onDone}
source={require("../assets/animations/done.json")}
style={styles.animation}
/>
)}
</View>
</Modal>
);
}
const styles = StyleSheet.create({
animation: { width: 150 },
container: {
alignItems: "center",
flex: 1,
justifyContent: "center",
},
});
export default UploadScreen;
And the component consuming the UploadScreen component is as follows:
import { StyleSheet } from "react-native";
import React, { useState } from "react";
import * as Yup from "yup";
import {
Form,
FormField,
FormImagePicker,
FormPicker as Picker,
SubmitButton,
} from "../components/forms";
import listingsApi from "../api/listings";
import Screen from "../components/Screen";
import CategoryPickerItem from "../components/CategoryPickerItem";
import useLocation from "../custom_hooks/useLocation";
import UploadScreen from "./UploadScreen";
const validationSchema = Yup.object().shape({
title: Yup.string().required().min(1).label("Title"),
price: Yup.number().required().min(1).max(10000000).label("Price"),
description: Yup.string().label("Description"),
category: Yup.object().required().nullable().label("Category"),
images: Yup.array().min(1, "Please select at least one image!"),
});
const categories = [
{
backgroundColor: "#fc5c65",
icon: "floor-lamp",
label: "Furniture",
value: 1,
},
{
backgroundColor: "#fd9644",
icon: "car",
label: "Cars",
value: 2,
},
];
function ListingEditScreen() {
const userLocation = useLocation();
const [uploadVisible, setUploadVisible] = useState(false);
const [progress, setProgress] = useState(0);
const handleSubmit = async (listing, { resetForm }) => {
setProgress(0);
setUploadVisible(true);
const result = await listingsApi.addListing(
{ ...listing, userLocation },
(progress) => setProgress(progress)
);
if (!result.ok) {
setUploadVisible(false);
return alert("Could not save the listing");
}
resetForm();
};
return (
<Screen style={styles.container}>
<UploadScreen
onDone={() => setUploadVisible(false)}
progress={progress}
visible={uploadVisible}
/>
</Screen>
);
}
export default ListingEditScreen;
You're not destructuring your props. The first argument to UploadScreen is the entire props object:
// onDone is your entire props object here.
function UploadScreen(onDone, progress = 0, visible = false) {
Add braces to pull out specific props:
// add the curlies to extract specific props
function UploadScreen({onDone, progress = 0, visible = false}) {
Destructure the props
function UploadScreen({onDone, progress, visible}) {
I want to increase and decrease the counter.counter1 and counter.counter2.innerCount by input value.
Here is the error I found now
I am weak at destructing object etc. and now learning for it.
Could provide me any advice or code? Especially for increment and decrement for innerCount. Much appreciate.
actionCreators.js
import * as actionTypes from "../actionTypes";
export const incrementCounter1 = () => {
return {
type: ActionTypes.INCREMENT_COUNTER_1,
};
};
export const decrementCounter1 = () => {
return {
type: ActionTypes.DECREMENT_COUNTER_1,
};
};
export const incrementByAmount = (amount) => {
return {
type: ActionTypes.INCREMENT_BY_AMOUNT,
amount:amount,
};
};
reducer.js
import * as actionTypes from "../actionTypes";
const INITIAL_STATE = {
counter: {
counter1: 0,
counter2: {
innerCount: 0,
},
},
};
export const Auth = (state = INITIAL_STATE, action) => {
const { type, payload } = action;
let a;
switch (type) {
case ActionTypes.INCREMENT_COUNTER_1:
a = {
...state,
counter: {
...state.counter,
counter1: state.counter.counter1 +=1,
},
};
return a;
case ActionTypes.DECREMENT_COUNTER_1:
a = {
...state,
counter: {
...state.counter,
counter1: state.counter.counter1 -=1,
},
};
return a;
case ActionTypes.INCREMENT_BY_AMOUNT:
a = {
...state,
counter: {
...state.counter,
counter1: state.counter.counter1 +=payload,
},
};
return a;
default:
return state;
}
};
export default Auth;
mainPage.js
import React, { useState } from "react";
import { View, Text, StyleSheet, Button, TextInput } from "react-native";
import {
incrementCounter1,
decrementCounter1,
incrementByAmount,
} from "./states/redux/ActionCreators/auth";
import { connect } from "react-redux";
const Counter = ({
counterRedux,
incrementCounter1,
decrementCounter1,
incrementByAmount,
}) => {
const [amount, setAmount] = useState('');
return (
<View>
<Text style={styles.text}>Input text for changing</Text>
<Button title="INCREMENT" onPress={() => incrementCounter1()} />
<Button title="DECREMENT" onPress={() => decrementCounter1()} />
<View>
<Text style={styles.Atext}>Enter amount to increase:</Text>
<TextInput style={styles.input} value={amount} onChangeText={(a) => setAmount(a)} />
<Text style={styles.Atext}>Amount: {amount}</Text>
<Button title='Add Amount' onPress={(amount)=>incrementByAmount(amount)}></Button>
</View>
<View>
<Text style={styles.Atext}>First Counter: {counterRedux.counter1}</Text>
</View>
</View>
);
};
const mapStateToProps = (state) => {
return {
counterRedux: state.counter,
};
};
const mapDispatchToProps = (dispatch) => {
return {
incrementCounter1: () => dispatch(incrementCounter1()),
decrementCounter1: () => dispatch(decrementCounter1()),
incrementByAmount: (amount) => dispatch(incrementByAmount(amount)),
};
};
const styles = StyleSheet.create({
text: {
fontSize: 25,
},
Atext: {
fontSize: 20,
},
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
input: {
borderWidth: 1,
borderColor: "#777",
padding: 8,
margin: 10,
width: 200,
},
button: {
backgroundColor: "#fff",
fontSize: 15,
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
actionTypes.js
export const INCREMENT_BY_AMOUNT = 'INCREMENT_BY_AMOUNT';
export const INCREMENT_COUNTER_1 = 'INCREMENT_COUNTER_1';
export const DECREMENT_COUNTER_1 = 'DECREMENT_COUNTER_1';
Your first issue is here: <Button title='Add Amount' onPress={(amount)=>incrementByAmount(amount)}></Button>.
What you are doing is passing to incrementByAmount the argument passed by onPress which is not at all the amount you expect, but a PressEvent object.
In order to receive the amount you expect, you need to do this: <Button title='Add Amount' onPress={() => incrementByAmount(amount)}></Button> so you get the amount from useState.
Also you definitely don't need to have three actions for your counter, a simpler way to do it would be to have an updateAmount function to which you pass as payload a type that would be "increment" or "decrement", and an amount.
An even simpler way would be to only have an amount and pass either a negative or a positive value to it.
For your increment and decrement buttons, you would simply need to pass 1 or -1 as amount.
Your second issue is that you are mutating your state in your reducer with the += and -= operators.
Here is your fixed reducer (I will let you implement the changes I suggested earlier though):
import * as actionTypes from "../actionTypes";
const INITIAL_STATE = {
counter: {
counter1: 0,
counter2: {
innerCount: 0,
},
},
};
export const Auth = (state = INITIAL_STATE, action) => {
const { type, payload } = action;
switch (type) {
case ActionTypes.INCREMENT_COUNTER_1:
return {
...state,
counter: {
...state.counter,
counter1: state.counter.counter1 + 1,
},
};
case ActionTypes.DECREMENT_COUNTER_1:
return {
...state,
counter: {
...state.counter,
counter1: state.counter.counter1 - 1,
},
};
case ActionTypes.INCREMENT_BY_AMOUNT:
return {
...state,
counter: {
...state.counter,
counter1: state.counter.counter1 + payload,
},
};
default:
return state;
}
};
export default Auth;
I removed the a variable that wasn't needed and changed the += operator to + and the -= operator to - so your state isn't mutated.
Your third issue is that you are destructuring a variable payload while it is called amount in your action creator.
Also don't forget that you are getting a string from <TextInput> and not a number.
Finally you import your action types as actionTypes but use them as ActionTypes.
I'm encountering this strange issue that I can figure out why is happing.
This should not be happening since the prop passed down to the History component has not been updated.
./components/History.js
...
const History = ({ previousLevels }) => {
return (
<ScrollView style={styles.container}>
{previousLevels.reverse().map(({ date, stressValue, tirednessValue }) => {
return (
<CardKBT
key={date}
date={date}
stressValue={stressValue}
tirednessValue={tirednessValue}
/>
)
})}
</ScrollView>
)
}
...
export default History
As can be seen in this code (below), the prop to the History is only updated once the user press Save.
App.js
import React from 'react'
import { View, ScrollView, StyleSheet } from 'react-native'
import { AppLoading, Font } from 'expo'
import Store from 'react-native-simple-store'
import { debounce } from 'lodash'
import CurrentLevels from './components/CurrentLevels'
import History from './components/History'
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
isLoadingComplete: false,
currentLevels: {
stressValue: 1,
tirednessValue: 1,
},
previousLevels: [],
}
this.debounceUpdateStressValue = debounce(this.onChangeStressValue, 50)
this.debounceUpdateTirednessValue = debounce(
this.onChangeTirednessValue,
50
)
}
async componentDidMount() {
const previousLevels = await Store.get('previousLevels')
if (previousLevels) {
this.setState({ previousLevels })
}
}
render() {
const { stressValue, tirednessValue } = this.state.currentLevels
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
...
/>
)
} else {
return (
<View style={{ flex: 1 }}>
<CurrentLevels
stressValue={stressValue}
onChangeStressValue={this.debounceUpdateStressValue}
tirednessValue={tirednessValue}
onChangeTirednessValue={this.debounceUpdateTirednessValue}
onSave={this.onSave}
/>
<History previousLevels={this.state.previousLevels} />
</View>
)
}
}
...
onChangeStressValue = stressValue => {
const { tirednessValue } = this.state.currentLevels
this.setState({ currentLevels: { stressValue, tirednessValue } })
}
onChangeTirednessValue = tirednessValue => {
const { stressValue } = this.state.currentLevels
this.setState({ currentLevels: { stressValue, tirednessValue } })
}
onSave = () => {
Store.push('previousLevels', {
date: `${new Date()}`,
...this.state.currentLevels,
}).then(() => {
Store.get('previousLevels').then(previousLevels => {
this.setState({
currentLevels: { stressValue: 1, tirednessValue: 1 },
previousLevels,
})
})
})
}
}
The component will re-render when one of the props or state changes, try using PureComponent or implement shouldComponentUpdate() and handle decide when to re-render.
Keep in mind, PureComponent does shallow object comparison, which means, if your props have nested object structure. It won't work as expected. So your component will re-render if the nested property changes.
In that case, you can have a normal Component and implement the shouldComponentUpdate() where you can tell React to re-render based on comparing the nested properties changes.
I have the following screen where I call a helper function pouchDB_helper.sync() to gather a bunch of data for me.
The goal is to be able to record where in the function it is currently at so I can give a percent or a status update in my render()
I'm new to react / react-native so I'm not sure if this is the right way to go about doing it. I'd like to be able to keep it as a helper function if possible because I use this function in other areas, this is just the only place I actually need a status update on where it's at in the process.
import React, { Component } from 'react';
import { ActivityIndicator, AsyncStorage, Button, StatusBar, Text, StyleSheet, View, } from 'react-native';
import * as pouchDB_helper from '../utils/pouchdb';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'flex-start',
justifyContent: 'center',
padding: "5%",
backgroundColor: "#fff",
width:"100%"
},
statusHeader: {
fontSize: 18,
fontWeight: "600",
marginBottom: 10,
textAlign:'center',
width:'100%'
}
});
type Props = {};
export default class SyncScreen extends Component<Props> {
static navigationOptions = {
title: 'Syncing Settings',
};
render() {
pouchDB_helper.sync().then((response) => {
//IT'S DONE
}, (error) => { alert("THERE WAS AN ERROR"); });
return (
<View style={styles.container}>
<Text style={styles.statusHeader}>Syncing, please wait..</Text>
<Text>WHERE I WANT TO CHANGE TEXT</Text>
</View>
);
}
}
pouchDB_helper example
note: This is just an example. I know the .get() won't take long enough to warrant a status but I'm just trying to understand the concept.
import React from 'react';
import { AsyncStorage } from 'react-native';
import PouchDB from 'pouchdb-react-native'
export async function sync() {
const company_id = await AsyncStorage.getItem('company_id');
const device_db = new PouchDB(company_id, {auto_compaction: true});
//STATUS UPDATE 1
return device_db.get("settings").then((s) => {
//STATUS UPDATE 2
return device_db.get("get_this").then((s) => {
//STATUS UPDATE 3
return device_db.get("get_that").then((s) => {
//STATUS UPDATE 4
}, (error) => { return false; });
}, (error) => { return false; });
}, (error) => { return false; });
}
Simple approach would be passing a function to the sync function which can change the state and set the desired text on component.
Example
constructor(props) {
super(props);
this.state = {
level: 'some init value'
};
}
onChangeState = (level) => {
this.setState({level});
}
componentDidMount() {
pouchDB_helper.sync(this.onChangeState).then((response) => {
//IT'S DONE
this.onChangeState('Finished');
}, (error) => { alert("THERE WAS AN ERROR"); });
}
render() {
return (
<View style={styles.container}>
<Text style={styles.statusHeader}>Syncing, please wait..</Text>
<Text>{`Current level is ${this.state.level}`}</Text>
</View>
);
}
export async function sync(changeState) {
const company_id = await AsyncStorage.getItem('company_id');
const device_db = new PouchDB(company_id, {auto_compaction: true});
//STATUS UPDATE 1
changeState(1);
return device_db.get("settings").then((s) => {
//STATUS UPDATE 2
changeState(2);
return device_db.get("get_this").then((s) => {
//STATUS UPDATE 3
changeState(3);
return device_db.get("get_that").then((s) => {
//STATUS UPDATE 4
changeState(4);
}, (error) => { return false; });
}, (error) => { return false; });
}, (error) => { return false; });
}
I'm using redux for the first time and something subtle is getting by me.
I have a container called Dashboard that displays two SimpleTabs. A simple tab is component that gets pressed and returns a number to its container for the item pressed. I can see actions being dispatched, event handler firing etc but the state being received in mapStateToProps never contains the item values. This might be why the render is never getting fired because the state is not changed.
Note: I've used the Ignite boilerplate as a starting point. It makes use of reduxsauce so the DashboardRedux.js may look a little unusual.
Dashboard.js
import React, { Component } from 'react'
import { ScrollView, Text, View, Image, TouchableOpacity, StyleSheet } from 'react-native'
import moment from 'moment'
import { Images, Colors, Metrics, ApplicationStyles } from '../Themes'
import SimpleTab from '../Components/SimpleTab'
import DashboardHeader from '../Components/DashboardHeader'
import DashboardActions from '../Redux/DashboardRedux'
import { connect } from 'react-redux'
export class Dashboard extends Component {
//TODO make numbers into enums
constructor(props) {
super(props)
this.updateTimeframe = this.updateTimeframe.bind(this)
this.updateAnalysisView = this.updateAnalysisView.bind(this)
const curTimeframe = 0
const curAnalysisView = 0
this.state = {curTimeframe, curAnalysisView}
}
// Event handler for timeframe tab
updateTimeframe(newValue) {
//newValue gets received as expected
this.props.updateTimeframe(newValue)
}
// Event handler for analysisview tab
updateAnalysisView(newValue) {
this.props.updateAnalysisView(newValue)
}
getUpdateTime = () => {
let s = moment().format("h:mm a")
return s
}
// Takes us back to login
openLoginScreen = () => {
//TODO does navigater have notion of <back>?
this.props.navigation.navigate('LoginScreen')
}
// For info on flex: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
render () {
let styles = ApplicationStyles.screen
/*
let localStyles = StyleSheet.create({
container: {
paddingBottom: Metrics.baseMargin
},
centered: {
alignItems: 'center'
}
})
console.log(styles)
*/
return (
//Problem: this.props.curTimeframe is always undefined
<View style={styles.mainContainer}>
<DashboardHeader updateTime={this.getUpdateTime()}></DashboardHeader>
<View style={{justifyContent: 'space-between'}} >
<SimpleTab
onSelect={this.updateTimeframe}
curTab={this.props.curTimeframe}
tabNames={["TODAY", "1W", "1M", "3M", "6M"]}
/>
</View>
<View style={{flex:1}} >
<Text style={{color: Colors.snow}}>
Analytical stuff for {this.props.curTimeframe} and {this.props.curAnalysisView}
</Text>
</View>
<View style={{height:60, justifyContent: 'space-between'}} >
<SimpleTab
onSelect={this.updateAnalysisView}
curTab={this.props.curAnalysisView}
tabNames={["HOME", "DAYPART", "REC", "INGRED", "SETTINGS"]}
/>
</View>
</View>
)}
}
const mapStateToProps = (state) => {
// Problem: state passed never contains curAnalysisView or curTimeframe
return {
curAnalysisView: state.curAnalysisView,
curTimeframe: state.curTimeframe
}
}
const mapDispatchToProps = (dispatch) => {
return {
updateTimeframe: newValue => dispatch(DashboardActions.updateTimeframe(newValue)),
updateAnalysisView: newValue => dispatch(DashboardActions.updateAnalysisView(newValue))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
DashboardRedux.js
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
updateTimeframe: ['newValue'],
updateAnalysisView: ['newValue'],
})
export default Creators
export const DashboardTypes = Types
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
curTimeframe: 0,
curAnalysisView: 0
})
/* ------------- Reducers ------------- */
export const updateTimeframe = (state, {newValue}) => {
//newValue gets passed as expected
return state.merge({curTimeframe: newValue});
}
export const updateAnalysisView = (state, {newValue}) => {
return state.merge({curAnalysisView: newValue});
}
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
[Types.UPDATE_TIMEFRAME]: updateTimeframe,
[Types.UPDATE_ANALYSIS_VIEW]: updateAnalysisView
})
SimpleTab.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { View, Text, Image, StyleSheet, TouchableHighlight } from 'react-native'
import { Colors, Metrics, Fonts, Images } from '../Themes/'
import styles from '../Themes/ApplicationStyles'
export default class SimpleTab extends Component {
static defaultProps = {
onSelect: null,
curTab: 0,
tabNames: ["Tab1", "Tab2", "Tab3"]
}
static propTypes = {
onSelect: PropTypes.func,
curTab: PropTypes.number,
tabNames: PropTypes.array
}
tabSelected = (tabNum) => {
this.props.onSelect(tabNum);
}
renderTabBar = () => {
let localStyles = StyleSheet.create({
unselectedText: {
marginTop: Metrics.baseMargin,
marginHorizontal: Metrics.baseMargin,
textAlign: 'center',
fontFamily: Fonts.type.base,
fontSize: Fonts.size.regular,
color: Colors.snow
},
selectedText: {
marginTop: Metrics.baseMargin,
marginHorizontal: Metrics.baseMargin,
textAlign: 'center',
fontFamily: Fonts.type.base,
fontSize: Fonts.size.regular,
fontWeight: 'bold',
color: Colors.fire
}
})
let result = []
for (i=0; i<this.props.tabNames.length; i++) {
let tabStyle = (i == this.props.curTab) ? localStyles.selectedText : localStyles.unselectedText
result.push(
<TouchableHighlight key={this.props.tabNames[i]} onPress={this.tabSelected.bind(this, i)}>
<Text style={tabStyle}>{this.props.tabNames[i]}</Text>
</TouchableHighlight>
)
}
return result
}
render () {
console.log("rendering tab")
return (
<View flexDirection='row' style={styles.contentContainer}>
{this.renderTabBar()}
</View>
)
}
}
MapStateToProps receives the new state properties via reducers. Your MapStatetoProps in Dashboard.js should look like below to get the new values.
const mapStateToProps = (state) => {
// Problem: state passed never contains curAnalysisView or curTimeframe
//new state values should be accessed via reducers..
return {
curAnalysisView: state.updateAnalysisView['curAnalysisView'],
curTimeframe: state.updateTimeframe['curTimeframe']
}
}
the mapStateToProps should like:
const mapStateToProps = (state) => {
const stateObj = state.toJS()
return {
curAnalysisView: stateObj.curAnalysisView,
curTimeframe: stateObj.curTimeframe
}
}
the .toJS() function converts it from immutable object to JS object.
Also, the reducer should have a default case that just returns the current state for when there is no action passed.
It turned out I needed to specify the "dashboard" branch of the state tree like this:
const mapStateToProps = (state) => {
return {
curAnalysisView: state.dashboard.curAnalysisView,
curTimeframe: state.dashboard.curTimeframe
}
}