Why state inside mapStateToProps functions is always "undefined"? - javascript

I am making a very basic counter app using react native and i am using redux in it for practice, but i am getting some problem.
Problem
In the mapStateToProps() function in my HomeScreen Component, the state argument, passed, is always getting a value of undefined, therefore my UI is also not updating. I used Redux DevTools and monitored the state and i get to know the state is changing absolutely fine on clicking the two buttons but inside mapStateToProps() function, it is always undefined.
Please correct me where I am going wrong.
This is my HomeComponent code
import React, { Component } from 'react';
import { View, StyleSheet, Text, Button } from 'react-native';
import { connect } from 'react-redux';
import * as counterActions from '../redux/actions/counterAction';
class HomeScreen extends Component{
render(){
return(
<View style={styles.homeView}>
<View>
<Text style={styles.homeText}>Basic Counter</Text>
</View>
<View style={styles.counterStyle}>
<View style={{marginRight: 20, width: 50}}>
<Button title="-" onPress={() => this.props.decreaseCounter()}></Button>
</View>
<Text style={{fontSize: 40, color: 'black'}}> {"Value = " + this.props.count} </Text>
<View style={{marginLeft: 20, width: 50}}>
<Button title="+" onPress={() => this.props.increaseCounter()} ></Button>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
homeView: {
flex: 1,
margin: 24,
alignItems: 'center',
},
homeText: {
color: 'black',
fontSize: 24,
},
counterStyle: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
},
});
function mapStateToProps(state) {
return {
count: state.count
};
}
function mapDispatchToProps(dispatch) {
return {
increaseCounter: () => dispatch(counterActions.increaseCounter()),
decreaseCounter: () => dispatch(counterActions.decreaseCounter())
};
}
export default connect(mapStateToProps,mapDispatchToProps)(HomeScreen);
This is my reducer function
const initialState = {
count: 0
};
export default function counterReducer(state = initialState, action) {
switch(action.type){
case "INCREASE_COUNTER":
return {
...state,
count: state.count + 1
};
case "DECREASE_COUNTER":
return {
...state,
count: state.count - 1
};
default:
return state;
}
}
This is my root level component
import React from 'react';
import { Provider } from 'react-redux';
import Main from './components/MainComponent';
import rootReducer from './redux/reducers/index';
import { createStore } from 'redux';
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default function App() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}

You must retrieve the count from the specific 'reducer' that you have state.counterReducer.count. In this case, your reducer is counterReducer.
Redux always assumes you have multiple reducers because each reducer is by itself and changing each reducer shouldn't update another reducer. Redux uses combineReducers for separating them.
you can read redux documentation about combining reducers

In order to answer this, you'll also need to show your rootReducer as well as the counterActions file. I'd likely guess there's an issue in one of those files if state isn't properly showing up in mapStateToProps. I've taken your example and moved it into a code sandbox where state and props are accurately reflected. Here are the changes I made. Keep in mind this is in the context of a web application, and not a react-native app, but since this is only dealing with state it shouldn't matter much.
App.js
import React from "react";
import { Provider } from "react-redux";
import { createStore } from "redux";
import Home from "./Home";
import rootReducer from "./reducers";
const store = createStore(rootReducer);
export default function App() {
return (
<Provider store={store}>
<Home />
</Provider>
);
}
Home.js
import React, { Component } from "react";
import { connect } from "react-redux";
import counterActions from "./reducers/counter/counterActions";
class HomeScreen extends Component {
render() {
return (
<div>
<div>
<p>Basic Counter</p>
</div>
<div>
<div>
<button onClick={this.props.decreaseCounter}>-</button>
</div>
<p style={{ fontSize: 40, color: "black" }}>
{`value= ${this.props.count}`}
</p>
<div style={{ marginLeft: 20, width: 50 }}>
<button onClick={this.props.increaseCounter}>+</button>
</div>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
count: state.counter.count
};
}
function mapDispatchToProps(dispatch) {
return {
increaseCounter: () => dispatch(counterActions.increaseCounter()),
decreaseCounter: () => dispatch(counterActions.decreaseCounter())
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomeScreen);
reducers/index.js
import { combineReducers } from "redux";
import counterReducer from "./counter/counterReducer";
const rootReducer = combineReducers({
counter: counterReducer
});
export default rootReducer;
reducers/counter/counterReducer.js
import { actionConstants } from "./counterActions";
const initialState = {
count: 0
};
export default function counterReducer(state = initialState, action) {
switch (action.type) {
case actionConstants.INCREASE_COUNTER:
return {
...state,
count: state.count + 1
};
case actionConstants.DECREASE_COUNTER:
return {
...state,
count: state.count - 1
};
default:
return state;
}
}
reducers/counter/counterActions.js
const actionConstants = {
INCREASE_COUNTER: "INCREASE_COUNTER",
DECREASE_COUNTER: "DECREASE_COUNTER"
};
const counterActions = {
increaseCounter() {
return {
type: actionConstants.INCREASE_COUNTER
};
},
decreaseCounter() {
return {
type: actionConstants.DECREASE_COUNTER
};
}
};
export { counterActions as default, actionConstants };
Heres's a link to the working sandbox

Related

How to export or use component state as hook

I want to use hook to get and set the value of my component WITHOUT PROPS. I import the component and it lists the data returned from the request, ok, but when I use the hook to see the data it returns empty, as if it were another instance.
Initially I used the state of the parent component, but when I needed to change some value of my component, everything would re-render as it affected the state of the parent component, so I want to isolate the state in the child component and use it freely as a hook elsewhere .
How I would like to use:
import MyComp, { useMyHook } from '../../../components/MyComp';
const OtherComp = () => {
const { data } = useMyHook();
return(
<div>
<button type="button" onClick={() => console.log(data)}>
click test
</button>
<MyComp />
</div>
);
};
export default OtherComp;
Component render
Example1
Example2
But the click button log: [ ]
Without using external components/libs like redux and etc.
My custom hook:
src/useMyHook.ts
import { useState } from 'react';
export const useMyHook = () => {
const [data, setData] = useState<any[]>([]);
const addItem = (item: unknown) => {
setData([...date, item]);
};
return { data, setData, addItem};
};
export default useMyHook;
My main component:
src/MyComp.tsx
import {useEffect} from 'react';
import useMyHook from './useMyHook';
const MyComp = () => {
const { data, setData } = useMyHook();
const req = async() => {
const {values} = await anyRequest(); // to do any request
setData(values);
};
useEffect(() => { req() },[]);
return(
<div>
{data.map((item) => <p>{item.name}</p>)}
</div>
);
};
export { useMyHook };
export default MyComp;
src/index.tsx
import MyComp, { useMyHook } from './MyComp';
export default MyComp;
export { useMyHook };
To demonstrate my comment:
import React,{useState, createContext} from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import Constants from 'expo-constants';
import useMyHook from './useMyHook'
import Example1 from './Example1'
import Example2 from './Example2'
import OtherComp from './OtherComp'
import {Data, DataSetter} from './types'
type DContext = {
data:Data,
setData:DataSetter
}
export const DataContext = createContext({
data:[],
setData:()=>{}
} as DContext)
export default function App() {
const [data, setData] = useState<Data>([])
return (
<View style={styles.container}>
<DataContext.Provider value={{data,setData}}>
<Example1/>
<Example2/>
<OtherComp/>
</DataContext.Provider>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
import { useContext, useCallback } from 'react';
import { DataContext } from './App';
export const useMyHook = () => {
const { data, setData } = useContext(DataContext);
// wrapped in useCallback to prevent function from
// being recreated
const addItem = useCallback((item: unknown) => {
setData(prev=>[...prev, item]);
},[]);
return { data, setData, addItem };
};
export default useMyHook;
useMyHook usage:
import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import useMyHook from './useMyHook';
export default function Example1() {
const { data, addItem } = useMyHook();
return (
<View style={{ width: '100%' }}>
<Button
title="Add Item From Example 1"
onPress={() => {
addItem(Math.floor(Math.random() * 25) + ' From Example1');
}}
/>
</View>
);
}
A demo

React Native 0.64.2, React-Redux 7.2.4, cannot get props to work "undefined is not an object"

I am trying to get React-Redux to work in my new React-Native app and am not succeeding. In my attempts to find online help, all of the examples are using Classes for each Component/Screen, but the beginning React Native template app is no longer using Classes. Everything is defined using "const" and the main App function is defined as:
`const App: () => Node = () => {...`
which are all new to me and I'm not sure if it has anything to do with my failures.
I have several Components/Screens all working nicely and do not have errors until I try to implement Redux.
Reducer:
const initState = {
syncedDate: '01/02/2022'
}
const projectsReducer = (state = initState, action) => {
switch (action.type) {
case 'PJS_SET_SYNCEDDATE':
return {
...state,
syncedDate: action.syncedDate
}
break;
default:
}
return state
}
export default projectsReducer;
Action:
export const pjsSetSyncedDate = (syncedDate) => {
return {
type: 'PJS_SET_SYNCEDDATE',
syncedDate
}
}
Store:
import { createStore, combineReducers } from 'redux';
import projectsReducer from '../reducers/projects';
const rootReducer = combineReducers({
projects: projectsReducer
});
const configureStore = () => {
return createStore(rootReducer);
};
export default configureStore;
App:
...
const store = configureStore();
...
const App: () => Node = () => {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<Provider store={store}>
<NavigationContainer>
<Tab.Navigator
...
</Tab.Navigator>
</NavigationContainer>
</Provider>
);
};
...
Component:
import React from 'react';
import type { Node } from 'react';
import {
Text,
View,
} from 'react-native';
import { connect } from 'react-redux';
export const DetailsScreen = ({ route, navigation }) => {
const { name } = route.params;
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details List</Text>
<Text>Incoming param: {JSON.stringify(name)}</Text>
<Text>SyncedDate: {this.props.syncedDate}</Text>
</View>
);
};
const mapStateToProps = (state) => {
return {
syncedDate: state.projects.syncedDate
}
};
export default ConnectedDetailsScreen = connect(mapStateToProps)(DetailsScreen);
The error occurs in the Text block
"this.props.syncedDate" - undefined is not an object
You are mixing some old implementation with the new one. try to change their step by step:
As a best practice, use payload property to pass your data through your action, so add it in your action.js:
export const pjsSetSyncedDate = (syncedDate) => {
return {
type: 'PJS_SET_SYNCEDDATE',
payload: syncedDate // -----> added here
}
}
So, change your reducer to get new syncedData with payload:
const initState = {
syncedDate: '01/02/2022'
}
const projectsReducer = (state = initState, action) => {
switch (action.type) {
case 'PJS_SET_SYNCEDDATE':
return {
...state,
syncedDate: action.payload.syncedDate // -------> add payload here
}
// break;
default:
}
return state
}
export default projectsReducer;
Note: you don't need the break expression in the switch/case since you returning the result in the case. I commented out this line in the above reducer code.
Now, back to the component DetailsScreen:
import React from 'react';
import { Text,View } from 'react-native';
import { useSelector } from 'react-redux'; // -----> import useSelector instead of connect method
export const DetailsScreen = ({ route, navigation }) => {
const { name } = route.params;
const syncedData = useSelector(state => state.projects.syncedData)
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details List</Text>
<Text>Incoming param: {JSON.stringify(name)}</Text>
<Text>SyncedDate: {syncedDate}</Text>
</View>
);
};
export default DetailsScreen;
Note: useSelector hook will get the state with a selector function. you define your reducer as projects in the combineReducers so your syncedData is in your state.projects
Note: with the above procedure, you don't need to connect your DetailsScreen to the store to get the state. useSelector hook will do that.

React Native Redux State Simply Not Working (Undefined)

TypeError: undefined is not an object (evaluating 'this.props.all_subjects.current').
Index.js
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
App.js
import 'react-native-gesture-handler';
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { StyleSheet } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import subjectsReducer from './reducers/subjectsReducer';
import Home from "./screens/home";
import Subjects from "./screens/subjects";
const store = createStore(subjectsReducer);
const Stack = createStackNavigator();
class App extends Component {
render() {
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
/>
<Stack.Screen
name="Subjects"
component={Subjects}
/>
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
}
}
export default App;
subjectsReducers.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
current: [],
all_subjects: [
'Literature',
'Speech',
'Writing',
'Algebra',
'Geometry',
'Statistics',
'Chemisrty',
'Biology',
'Physics',
'Economics',
'Geography',
'History',
],
};
const subjectsReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SELECT_SUBJECT':
// copy the state
const { current, all_subjects,} = state;
//remove a subject from the all_subjects array
const addedSubject = all_subjects.splice(action.payload, 1);
// put subject in current array
current.push(addedSubject);
// update the redux state to reflect the change
const newState = { current, all_subjects };
//return new state
return newState;
default:
return state
}
};
export default combineReducers({
subjects: subjectsReducer
});
subjectsActions.js
export const addSubject = subjectsIndex => (
{
type: 'SELECT_SUBJECT',
payload: subjectsIndex,
}
);
Subjects.js
import React from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Button } from 'react-native';
class Subjects extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Select Subjects Below!</Text>
{this.props.subjects.all_subjects.map((subject, index) => (
<Button
key={ subject }
title={ `Add ${ subject }` }
onPress={() =>
this.props.addSubject(index)
}
/>
))}
<Button
title="Back to home"
onPress={() =>
this.props.navigation.navigate('Home')
}
/>
</View>
);
}
}
// const mapStateToProps = (state) => {
// const { subjects } = state
// return { subjects }
// };
const mapStateToProps = state => ({
subjects: state.subjects
});
export default connect(mapStateToProps)(Subjects);
Home.js
import React from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Button } from 'react-native';
class Home extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>You have { this.props.all_subjects.current.length } subjects.</Text>
<Button
title="Select more subjects"
onPress={() =>
this.props.navigation.navigate('Subjects')
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text:{
color: '#888',
fontSize: 18
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
submitButton: {
backgroundColor: '#7a42f4',
padding: 10,
margin: 15,
height: 40,
},
submitButtonText:{
color: 'white'
}
});
// const mapStateToProps = (state) => {
// const { subjects } = state
// return { subjects }
// };
const mapStateToProps = state => ({
subjects: state.subjects
});
export default connect(mapStateToProps, null)(Home);
I am new to react native. I have searched everywhere for a solution for this issue, but I can’t seem to get anywhere. I have used redux in standard react, but I don’t know why this isn’t working. Any thoughts or suggestions would be greatly appreciated.

Redux React Native - Action not passing data to reducer

I am new to Redux and it might be some silly error. I am trying to make an Api call in Action and pass the data to the reducer. I can see the response from the api call but for some reason it's not sharing the data correctly with the reducer or I don't know how to pass and render the state properly to home.js. Please find below action - reducers - store.js - home.js
Action file
export const DATA_AVAILABLE = 'DATA_AVAILABLE';
export function getData(){
return (dispatch) => {
//Make API Call
fetch("MY API URL").then((response) => {
return response.json();
}).then((data) => {
var data = data.articles;
console.log(data)
dispatch({type: DATA_AVAILABLE, data:data});
})
};
}
REDUCERS
import { combineReducers } from 'redux';
import { DATA_AVAILABLE } from "../actions/" //Import the actions types constant we defined in our actions
let dataState = {
data: [],
loading:true
};
const dataReducer = (state = dataState, action) => {
switch (action.type) {
case DATA_AVAILABLE:
state = Object.assign({}, state, { data: action.data, loading:false });
console.log(dataState)
return state;
default:
return state;
}
};
// Combine all the reducers
const rootReducer = combineReducers({
dataReducer
// ,[ANOTHER REDUCER], [ANOTHER REDUCER] ....
})
export default rootReducer;
STORE.JS
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../app/reducers/index'; //Import the reducer
// Connect our store to the reducers
export default createStore(reducers, applyMiddleware(thunk));
HOME.JS
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
FlatList,
View,
Text,
ActivityIndicator
} from 'react-native';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions'; //Import your actions
class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
this.renderItem = this.renderItem.bind(this);
}
componentDidMount() {
this.props.getData(); //call our action
}
render() {
if (this.props.loading) {
return (
<View style={styles.activityIndicatorContainer}>
<ActivityIndicator animating={true}/>
</View>
);
} else {
console.log(this.state)
return (
<View style={{flex:1, backgroundColor: '#F5F5F5', paddingTop:20}}>
<FlatList
ref='listRef'
data={this.props.data}
renderItem={this.renderItem}
keyExtractor={(item, index) => index}/>
</View>
);
}
}
renderItem({item, index}) {
return (
<View style={styles.row}>
<Text style={styles.title}>
{this.props.data.title}
</Text>
<Text style={styles.description}>
</Text>
</View>
)
}
};
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.dataReducer.loading,
data: state.dataReducer.date
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(Home);
You are not mutating the state righty.
Redux do only shallow comparison for optimisation.
Its only check reference.
Reference need to be update.
state = Object.assign({}, state, {
data: [
...state.data, //change previous state data reference
...action.data //update current state data reference
],
loading: false
});

How to open and close an inline dialog in a react redux app

I have a working inline dialog using react state. The working code is below.
import React, { PureComponent } from 'react';
import { render } from 'react-dom';
import PropTypes from 'prop-types';
import Button from '#atlaskit/button';
import InlineDialog from '#atlaskit/inline-dialog';
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center',
};
class ButtonActivatedDialog extends PureComponent {
static propTypes = {
content: PropTypes.node,
position: PropTypes.string,
}
state = {
isOpen: false,
};
handleClick = () => {
this.setState({
isOpen: !this.state.isOpen,
});
}
handleOnClose = (data) => {
this.setState({
isOpen: data.isOpen,
});
}
render() {
return (
<InlineDialog
content={this.props.content}
position={this.props.position}
isOpen={this.state.isOpen}
onClose={this.handleOnClose}
>
<Button
onClick={this.handleClick}
isSelected
>
The Button
</Button>
</InlineDialog>
);
}
}
const App = () => (
<ButtonActivatedDialog
content={
<div>
<h5>
Displaying...
</h5>
<p>
Here is the information I need to display.
</p>
</div>}
position='bottom right'
/>
);
render(<App />, document.getElementById('root'));
I would like to have the same behavior with the button but using redux to maintain the state of the dialog.
After reading some material I believe I need to dispatch an action that will activate a reducer witch in turn will help me update the state of the dialog. However, I don't believe I fully understand how this should be put together.
Here is my work in progress but for some reason my codeSanbox does not like the format in which I'm creating the store.
mport React, { PureComponent } from 'react';
import { render } from 'react-dom';
import PropTypes from 'prop-types';
import Button from '#atlaskit/button';
import InlineDialog from '#atlaskit/inline-dialog';
import { connect, createStore } from 'react-redux'
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center',
};
const mapStateToProps = state => {
return {
isDialogOpen: false,
}
}
const mapDispatchToProps = dispatch => {
return {
toggleDialog: () => dispatch({
type: 'TOGGLE_DIALOG'
})
}
}
// action:
const tottleDialog = 'TOGGLE_DIALOG';
//action creator
const toggleDialog = (e) => ({
type: 'TOGGLE_DIALOG',
e,
})
class ButtonActivatedDialog extends PureComponent {
static propTypes = {
content: PropTypes.node,
position: PropTypes.string,
}
state = {
isOpen: false,
};
handleClick = () => {
this.setState({
isOpen: !this.state.isOpen,
});
}
handleOnClose = (data) => {
this.setState({
isOpen: data.isOpen,
});
}
render() {
return (
<InlineDialog
content={this.props.content}
position={this.props.position}
isOpen={this.state.isOpen}
onClose={this.handleOnClose}
>
<Button
onClick={this.handleClick}
isSelected
>
The Button
</Button>
</InlineDialog>
);
}
}
const App = () => (
<ButtonActivatedDialog
content={
<div>
<h5>
Displaying...
</h5>
<p>
Info here
</p>
</div>}
position='bottom right'
/>
);
const store = createStore(toggleDialog, {})
//need and action
//need an action creator - a function that returns an action:
//
// render(<App />, document.getElementById('root'));
render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root')
);
Ok so first you have to set up your redux state. We usually do this according to the re-ducks pattern here: https://github.com/alexnm/re-ducks
This means you will create a directory for each "part" of your application. Each part then has a:
Operation: To perform tasks on the state (like open up or close your inline menu)
Selector: To get some value of the state (like is the inline menu open?)
Action: To perform an action on the state (like set isOpen to true/false)
Reducer: To apply an action to the state (like the one from above)
Type: Any type of state change. Any action has a type, and the type decides which part in the reducer is executed.
So in your example, I would create a state/inlineMenu folder and inside it the following files:
actions.js:
import types from './types';
const toggleState = {
type: types.TOGGLE_STATE
};
export default {
updateMenuState
}
operations.js:
import actions from './actions';
const toggleState = actions.toggleState;
export default {
updateMenuState
};
reducers.js:
import types from './types';
const initialState = {
isOpen: false // closed per default
};
const inlineMenuReducer = (state = initialState, action) => {
switch (action.type) {
case types.TOGGLE_STATE:
return { ...state, isOpen: !state.isOpen }
default:
return state;
}
};
export default inlineMenuReducer;
selectors.js:
const isMenuOpen = state => state.inlineMenu.isOpen;
export default {
isMenuOpen
};
types.js:
const TOGGLE_STATE = 'inlineMenu/TOGGLE_STATE';
export default {
TOGGLE_STATE
};
index.js:
import reducer from './reducers';
export { default as inlineMenuSelectors } from './selectors';
export { default as inlineMenuOperations } from './operations';
export default reducer;
You also have to set up the default provider. Your path to the isOpen property in the selectors should probably be adjusted.
Now you have your global redux state set up.
We need to get the data in it now to the view. We need to use redux' connect function for this, in which it will take the operations and selectors and map them to the default react props.
So your connected component could look like this:
import React, { PureComponent } from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Button from '#myKitkit/button';
import InlineDialog from '#mykit/inline-dialog';
import { inlineMenuOperations, inlineMenuOperations } from '../path/to/state/inlineMenu';
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center',
};
class ButtonActivatedDialog extends PureComponent {
static propTypes = {
content: PropTypes.node,
position: PropTypes.string,
toggleState: PropTypes.func.isRequired
}
handleClick = () => {
const { toggleState } = this.props;
// This will dispatch the TOGGLE_STATE action
toggleState();
}
handleOnClose = () => {
const { toggleState } = this.props;
// This will dispatch the TOGGLE_STATE action
toggleState();
}
render() {
return (
<InlineDialog
content={this.props.content}
position={this.props.position}
isOpen={this.props.isOpen}
onClose={this.handleOnClose}
>
<Button
onClick={this.handleClick}
isSelected
>
The Button
</Button>
</InlineDialog>
);
}
}
// You need to add the provider here, this is described in the redux documentation.
const App = () => (
<ButtonActivatedDialog
content={
<div>
<h5>
Displaying...
</h5>
<p>
Here is the information I need to display.
</p>
</div>}
position='bottom right'
/>
);
const mapStateToProps = state => ({
isOpen: inlineMenuSelectors.isOpen(state);
});
const mapDispatchToProps = dispatch => ({
toggleState: () => dispatch(inlineMenuOperations.toggleState())
}
const ConnectedApp = connect(mapStateToProps, mapDispatchToProps);
render(<ConnectedApp />, document.getElementById('root'));

Categories