React Redux: state undefined - reactjs

Playing around with react-redux and my state isCartVisible is showing undefined, I used simple functional components and I'm storing my stores in different files.
//main index.js file
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import store from './redux-store/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Provider store={store}><App /></Provider>);
and
//App.js
import { useSelector } from "react-redux";
import Layout from "./components/Layout/Layout";
import Cart from "./components/Cart/Cart";
function App() {
const cartVisible = useSelector((state) => state.isCartVisible);
return (
<Layout>
{cartVisible && <Cart />}
</Layout>
);
}
and a component deep somewhere inside the app, by clicking the button I wanna toggle my <Cart> component
//CartButton.js
import { useDispatch } from "react-redux";
const CartButton = () => {
const dispatch = useDispatch();
const cartShowHandler = () => {
dispatch({ type: "cartToggle" });
};
return (
<button onClick={cartShowHandler}>
Click
</button>
);
};
and that's my store file, where I've created my store with reducer
import { createStore } from "redux";
const uiReducer = (state = { isCartVisible: true }, action) => {
if (action.type === "cartToggle") {
state.isCartVisible = !state.isCartVisible;
}
return state;
};
const uiStore = createStore(uiReducer);
export default uiStore;

You should never mutate the state. Your condition in reducer should look like this and it will work.
if (action.type === "cartToggle") {
return { ...state, isCartVisible: !state.isCartVisible};
}
As you have only one key in store in your example at the moment. You can do it this way also.
return { isCartVisible: !state.isCartVisible};
But it's always a good practice to return the whole state in your reducer's conditions.
Remember that redux do shallow comparison. Which means it checks if reference of an object is changed. In your case it wasnt changed.
Once, I wrote something about this topic in a blog post https://dev.to/machy44/shallow-comparison-in-redux-3a6

In Redux, you can only have one store. So it is very likely that your useSelector call actually tries to select data from another store than you are expecting it here.
You could validate that by using something like
const fullState = useSelector(state => state)
console.log(fullState)
That said, you are also writing a style of Redux here that is outdated by many years - in modern Redux you are not writing switch..case reducers or string action types. Also, createStore is deprecated at this point in favor of configureStore.
I would highly recommend you read about modern Redux and then follow the official Redux tutorial.
Whatever resources you have been following right now might have given you a very skewed view of how to use Redux.

Related

useSelector not updating with Redux Toolkit

I've been battling this all day long and I'd appreciate any help.
I have a redux store built with Redux Toolkit and createSlice that looks like so:
const initialState = {
analiticaNumber: "",
animal: {},
tests: [{ key: "G9116", value: "DERMATOFITOS PCR/ MUESTRA" }],
};
const PeticionSlice = createSlice({
name: "peticion",
initialState,
reducers: {
addTest: (state, action) => {
state.tests.push(action.payload);
},
},
});
export const { addTest: addTestActionCreator } = PeticionSlice.actions;
export const testsArray = (state) => state.Peticion.tests;
export default PeticionSlice.reducer;
I also have a root reducer that imports the rest of the slices and names them as such
import { combineReducers } from "redux";
import NavigationSlice from "./NavigationSlice";
const RootReducer = combineReducers({
Peticion: PeticionSlice,
});
export default RootReducer;
When I add tests to the tests array it works fine and shows in the redux devtools.
The promblem comes that react does not see the change in the store and won't update the child component:
import { testsArray } from "./Store/PeticionSlice";
That's how I import namely the testsArray to call with the useSelector.
The tradicional way of const { tests } = useSelector( (state) => state.Peticion) doesn't work either.
function App() {
const tests = useSelector(testsArray);
useEffect(() => {
console.log("tests");
}, [tests]);
return (
<StylesProvider injectFirst>
<div className="App">
<nav>
<Navbar />
</nav>
{tests.map((test) => (
<p>{test.key}</p>
))}
</div>
</StylesProvider>
);
}
I belive it has to do something with the mutability of the state, but I thought the toolkit took care of that, and for the life of me I don't know how to solve this.
Any help??? Thanks a lot.
** UPDATE **
I believe it has to do with the way I dispatch the actions. Because I needed to add several boundaries to what the app does, I decided to have an external function that filters and dispatches accordingly. It is not a react component.
import { configureStore } from "#reduxjs/toolkit";
import { addTestToList, addTestActionCreator } from "../Store/PeticionSlice";
import RootReducer from "../Store/RootReuder";
const PruebasToSubmitArray = [];
const store = configureStore({
reducer: RootReducer,
});
const handleTestList = (test, action) => {
const anatomia = "A";
const microbiologia = "M";
function oneBiopsia() {
while (test.key.toString().charAt(0) === anatomia) {
return PruebasToSubmitArray.some(
(pruebaInArray) => pruebaInArray.key.toString().charAt(0) === anatomia
);
}
return false;
}
if (!oneBiopsia() && action === "add") {
switch (test.key.toString().charAt(0)) {
case anatomia:
// console.log("Open pdf for anatomia");
store.dispatch(addTestActionCreator(test));
break;
case microbiologia:
// console.log("Open pdf for micro");
store.dispatch(addTestActionCreator(test));
break;
default:
// console.log("add test to the list, ", test);
store.dispatch(addTestActionCreator(test));
break;
}
} else if (action === "remove") {
// console.log("remove test from the list, ", test);
} else if (oneBiopsia()) {
console.log("Only one biopsia per peticion, ", newState);
}
return null;
};
export default handleTestList;
I added a button on App component and it worked as expected (i showed the updated state), as is right now redux updates the state but the component won't reflect it.
Code SandBox as complete as I can
Very odd behavior in my case.
I did
state = action.payload
and that didn't work.
Once I switched to
state.viewer = action.payload.viewer
everything worked!
Multiple Instances of Store
You create a store variable in your index.js file and pass that store to the react-redux Provider component. This is the store instance which all react-redux useSelector and useDispatch hooks will interact with.
In your HandleTestList.js file you create a different store variable. You then dispatch actions to that store, but those actions won't be reflected in your React app because this isn't the store that your React app uses.
handleTestList needs to either A) import the same global store variable. You will want to move this out of index.js and into store.js to avoid circular dependencies. or B) accept dispatch as an argument.

How to access the Redux store outside of a component in React

I am beginning with Redux and I always used it in components with connect() and mapStateToProps(), but now I want to call my API with setInterval() every x time to check if the server has new data not stored in the Redux store, and substitute it.
My approach was to create a function that reads the store and update it like that:
import { store } from './dir/dir/store.js'
const refresher = async () => {
const state = store.getState();
// Call API, compare 'state.calendar' with calendar from server
// Call store.dispatch() if they are different, then update Redux store
}
export default refresher
My questions are:
Is this a good practise to use Redux?
Is there a better approach to this problem?
Thanks
It's perfectly fine to export the store and use within a vanilla js/ts file.
Example Redux Store
Make sure to export the store that you create
import { configureStore } from "#reduxjs/toolkit";
import { slice } from "../features/counterSlice";
export const store = configureStore({
reducer: {
counter: slice.reducer
}
});
Usage in Non-Component Code:
You can then import the created store in any other code
import { store } from "../App/store";
import { slice as counterSlice } from "../features/counterSlice";
export function getCount(): number {
const state = store.getState();
return state.counter.value;
}
export function incrementCount() {
store.dispatch(counterSlice.actions.increment());
}
Traditional Usage in Functional Component
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../App/store";
import { slice as counterSlice } from "../features/counterSlice";
export function Clicker() {
const dispatch = useDispatch();
const count = useSelector((state: RootState) => state.counter.value);
const dispatchIncrement = () => dispatch(counterSlice.actions.increment())
// ...
Example Slice
import { createSlice } from "#reduxjs/toolkit";
export const slice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
}
}
});
Demo in Codesandbox
Note: You cannot use this option with Server Side Rendering. If you need to support SSR, you can use middleware to listen to dispatched actions and handle elsewhere.
Further Reading
What is the best way to access redux store outside a react component? | Stack Overflow
Access the Redux Store Outside a React Component | Blog
How can I access the store in non react components? | Github Issues
Here you can access the store and action out side any component like index.js file in react-native.
import {updateLocationAlertItem} from './src/store/actions/locationAlertAction';
import {store} from './src/store/index';
store.subscribe(listener);
function listener() {
state = store.getState().locationAlertReducer;
}
store.dispatch(
updateLocationAlertItem({
index: index,
is_in_radius: true,
is_notification: true,
arrival_time: moment().format('DD/MM/YYYY hh:mm'),
exit_time: item.exit_time,
}),
);

Handling button state and actions in redux

I am new to react and redux.
Overview of app: I have a ToggleButtonGroup with two buttons. When a button is click I want to output a table for the respective button. The tables we filtered based on the button click.
Question: I am not sure how to setup an action, state and reducer in my project for the button functionality. My button is a component. Is it best practice to use actions and reducers for buttons? How would I pass these actions to other components? Any examples or resources is appreciated.
This is my Button.tsx file
import React, { Component, useState } from 'react'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup';
import ToggleButton from 'react-bootstrap/ToggleButton';
function ToggleButtonGroupControlled() {
const [value, setValue] = useState([1, 2]);
return (
<ToggleButtonGroup type="checkbox" value={value} onChange={() => setValue(value)}>
<ToggleButton value={1}>PA Probes</ToggleButton>
<ToggleButton value={2}>Convential Probes</ToggleButton>
</ToggleButtonGroup>
);
}
export class Buttons extends Component {
render() {
return (
<div>
<ToggleButtonGroupControlled />
</div>
)
}
}
export default Buttons
This is my types.ts file
export const TOGGLE_PA_PROBES = 'TOGGLE_PA_PROBES';
export const TOGGLE_CONVENTIONAL_PROBES = 'TOGGLE_CONVENTIONAL_PROBES';
This is buttonAction.ts file
import{ TOGGLE_PA_PROBES, TOGGLE_CONVENTIONAL_PROBES } from './types';
export function togglePAProebs(){
}
export function toggleConventionalProbes(){
}
This is my buttonReducers.ts file
import{ TOGGLE_PA_PROBES, TOGGLE_CONVENTIONAL_PROBES } from '../Actions/types';
export function ButtonReducer(state, action){
switch(action.type){
default:
return state;
}
}
I will assume you know the basics of redux and react-redux. In short, you know that an action is dispatched to a store.
In a real world situation, it is as if you change a channel on your TV, (1) you dispatch a CHANGE_CHANNEL action to your remote control, (2) the remote control will receive the action dispatched and send that action with the channel information to your TV (reducer), then (3) your TV (reducer) will talk to your cable provider (store), and return the channel data back if it is available to you.
That flow is important because it can be used in your example as well. To begin with, imagine you have a store with some probes. On toggling your checkbox, you will dispatch an action FILTER, which will filter out the probes that need to be shown from a database list, for example, then call your reducer with the filtered probes to update your store and send that data back to your dispatcher.
When you first load your page, I also imagine you want to show the list of all your probes, so you would also need a FETCH action to be dispatched upon componentDidMount. Having that in mind, we can come up with a types file like this:
types.js
export const TOGGLE_PROBES = "TOGGLE_PROBES";
export const LOAD_PROBES = "LOAD_PROBES";
Now that the types are defined, we can create our actions,
import { TOGGLE_PROBES, LOAD_PROBES } from "./types";
const dbProbes = [
{ title: "pa probe 1", type: 1 },
{ title: "pa probe 2", type: 1 },
{ title: "conditional probe 1", type: 2 },
{ title: "conditional probe 1", type: 2 }
];
function toggleProbes(filtered) {
return {
type: TOGGLE_PROBES,
filtered: filtered
};
}
function loadProbes(probes) {
return {
type: LOAD_PROBES,
probes: probes
};
}
export function fetchProbes() {
return function(dispatch) {
dispatch(loadProbes(dbProbes));
};
}
export function filterProbes(filter) {
return function(dispatch) {
const filtered = dbProbes.filter(probe => filter.includes(probe.type));
dispatch(toggleProbes(filtered));
};
}
Notice that I created a fake list of probes called dbProbes. In a real world situation, you would probably be reaching out to a database to filter your probes.
After having all of the actions set up, you can finally work on the reducer and update your state accordingly,
import { TOGGLE_PROBES, LOAD_PROBES } from "./types";
const initialState = {
probes: []
};
function ButtonReducer(state = initialState, action) {
switch (action.type) {
case TOGGLE_PROBES:
return { ...state, probes: action.filtered };
case LOAD_PROBES:
return { ...state, probes: action.probes };
default:
return state;
}
}
export default ButtonReducer;
In order to be able to dispatch this in your components, you do need to set redux up. In your index.js, you could accomplish that by using both react, react-redux and react-thunk. React-thunk is a middleware which is often used with redux for async calls.
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import "./styles.css";
import Buttons from "./Buttons";
import rootReducer from "./store/reducer";
const store = createStore(rootReducer, applyMiddleware(thunk));
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<Buttons />
</Provider>,
rootElement
);
First, in your Button components, maybe you just want to show the initial list first, so to do that you need to use a react-redux high order component called connect, which will, as the name states, connect your store and map your store's state to dispatchers and props.
import React, { Component } from "react";
import ToggleButtonGroupControlled from "./ToggleButtonGroupControlled";
import { connect } from "react-redux";
import { fetchProbes } from "./store/action";
export class Buttons extends Component {
componentDidMount() {
this.props.fetchProbes();
}
render() {
let probes = null;
if (this.props.probes) {
probes = this.props.probes.map(probe => <li>{probe.title}</li>);
}
return (
<div>
<ToggleButtonGroupControlled />
<ul>{probes}</ul>
</div>
);
}
}
function mapStateToProps(state) {
return {
probes: state.probes
};
}
function mapDispatchToProps(dispatch) {
return {
fetchProbes: () => dispatch(fetchProbes())
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Buttons);
Finally, your toggle button component can properly dispatch a TOGGLE_FILTER action to your actions.js, which will filter the probes, then send the action to reducer, which will update your state and return the updated state, which at this point is mapped to your component props in your Button.js component.
import React, { useState } from "react";
import { ToggleButtonGroup } from "react-bootstrap";
import { ToggleButton } from "react-bootstrap";
import { connect } from "react-redux";
import { filterProbes } from "./store/action";
function ToggleButtonGroupControlled(props) {
const [value, setValue] = useState([1, 2]);
const toggleChangeHandler = newValue => {
setValue(newValue);
props.filterProbes(newValue);
};
return (
<ToggleButtonGroup
type="checkbox"
value={value}
onChange={e => toggleChangeHandler(e)}
>
<ToggleButton value={1}>PA Probes</ToggleButton>
<ToggleButton value={2}>Convential Probes</ToggleButton>
</ToggleButtonGroup>
);
}
function mapDispatchToProps(dispatch) {
return {
filterProbes: types => dispatch(filterProbes(types))
};
}
export default connect(
null,
mapDispatchToProps
)(ToggleButtonGroupControlled);
The cool thing about redux is that it allows for a global state, so you dispatch and change your list of probes from the ToggleButtonGroupControlled component, but that store (list of probes) it's also available for other components that you decide to connect to your store, in this case, Button.js.
You can check the final code running and ready for your problem here:
To understand more about redux-thunk, go here: https://github.com/reduxjs/redux-thunk
Also, there's a free redux book here that you could maybe take a quick look at: https://leanpub.com/redux-book
Redux might be overkill for this example, it all depends however at what level in the component hierarchy your table lives in relation to your ToggleButtonGroupControlled component.
It would help in your question to see the table component, is it in a separate file / route?
I would start simple and pass the value up out of your ToggleButtonGroupControlled component by creating an onChange prop. Right now your ToggleButtonGroupControlled component doesn't add much more functionality
than the third party ToggleButtonGroup component so it's a little hard to reason about.
Also if you do choose to go the redux route because you need this data in the global state then you can probably forego the local state you have here and select it from your redux store and pass into the component

I am using React Context and would like to get confirmed my structure

It is my first application using react context with hooks instead of react-redux and would like to get help of the structure of the application.
(I'm NOT using react-redux or redux-saga libraries.)
Context
const AppContext = createContext({
client,
user,
});
One of actions example
export const userActions = (state, dispatch) => {
function getUsers() {
dispatch({ type: types.GET_USERS });
axios
.get("api address")
.then(function(response) {
dispatch({ type: types.GOT_USERS, payload: response.data });
})
.catch(function(error) {
// handle error
});
}
return {
getUsers,
};
};
Reducer (index.js): I used combineReducer function code from the redux library
const AppReducer = combineReducers({
client: clientReducer,
user: userReducer,
});
Root.js
import React, { useContext, useReducer } from "react";
import AppContext from "./context";
import AppReducer from "./reducers";
import { clientActions } from "./actions/clientActions";
import { userActions } from "./actions/userActions";
import App from "./App";
const Root = () => {
const initialState = useContext(AppContext);
const [state, dispatch] = useReducer(AppReducer, initialState);
const clientDispatch = clientActions(state, dispatch);
const userDispatch = userActions(state, dispatch);
return (
<AppContext.Provider
value={{
clientState: state.client,
userState: state.user,
clientDispatch,
userDispatch,
}}
>
<App />
</AppContext.Provider>
);
};
export default Root;
So, whenever the component wants to access the context store or dispatch an action, this is how I do from the component :
import React, { useContext } from "react";
import ListMenu from "../common/ListMenu";
import List from "./List";
import AppContext from "../../context";
import Frame from "../common/Frame";
const Example = props => {
const { match, history } = props;
const { userState, userDispatch } = useContext(AppContext);
// Push to user detail route /user/userId
const selectUserList = userId => {
history.push(`/user/${userId}`);
userDispatch.clearTabValue(true);
};
return (
<Frame>
<ListMenu
dataList={userState.users}
selectDataList={selectUserList}
/>
<List />
</Frame>
);
};
export default Example;
The problem I faced now is that whenever I dispatch an action or try to access to the context store, the all components are re-rendered since the context provider is wrapping entire app.
I was wondering how to fix this entire re-rendering issue (if it is possible to still use my action/reducer folder structure).
Also, I'm fetching data from the action, but I would like to separate this from the action file as well like how we do on redux-saga structure. I was wondering if anybody know how to separate this without using redux/redux-saga.
Thanks and please let me know if you need any code/file to check.
I once had this re-rendering issue and I found this info on the official website:
https://reactjs.org/docs/context.html#caveats
May it will help you too
This effect (updating components on context update) is described in official documentation.
A component calling useContext will always re-render when the context value changes. If re-rendering the component is expensive, you can optimize it by using memoization
Possible solutions to this also described
I see universal solution is to useMemo
For example
const Example = props => {
const { match, history } = props;
const { userState, userDispatch } = useContext(AppContext);
// Push to user detail route /user/userId
const selectUserList = userId => {
history.push(`/user/${userId}`);
userDispatch.clearTabValue(true);
};
const users = userState.users;
return useMemo(() => {
return <Frame>
<ListMenu
dataList={users}
selectDataList={selectUserList}
/>
<List />
</Frame>
}, [users, selectUserList]); // Here are variables that component depends on
};
I also may recommend you to completly switch to Redux. You're almost there with using combineReducers and dispatch. React-redux now exposes useDispatch and useSelector hooks, so you can make your code very close to what you're doing now (replace useContext with useSelector and useReducer with useDispatch. It will require minor changes to arguments)

Integrating Dispatch Actions in Container Component Pattern

So I'm completely confused on how to integrate the Container and Component Pattern. I've been reviewing examples all morning and nothing seems to be clicking. How I have been worked with React previously on my first project was fetch the data within my view components and then pass that data down as props using the #connect which works, but in an "automagically" way to me at this time.
import React;
...
import {action} from 'path/to/action.js';
#connect((store) => {return{ key: store.property}});
export class Component{
componentWillMount(){
this.props.dispatch(action());
}
}
As I'm working more with React I want to learn the more "correct" way of building out with Redux and understand on a deeper level what is happening.
What I have setup is
index.jsx (This renders all of my HOCs)
|
App.jsx (Container)
|
Auth.jsx (Component)
|
Layout.jsx (Component) - Contains app content
--or--
AuthError.jsx (Component) - 401 unauthenticated error page
Authentication is handled through an outside resource so this app will not control anything with Logging in or out. There will be no log in/out states simply receiving an object from an API that identifies the User Role & Authenticated Boolean.
What I would like to happen is when the App loads, it will fetch data from a mock API, JSON Server. From there it will render the Auth component. The Auth component will take in props from App.jsx and either render the Layout.jsx or AuthError.jsx.
Where I'm running into issues is how this should be integrated. I'm going to omit lines of code I don't think absolutely pertain to the question.
store.js
import { applyMiddleware, combineReducers, createStore } from 'redux';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import promise from 'redux-promise-middleware';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from './reducers';
const middleware = applyMiddleware(promise(), thunk, createLogger());
export default createStore(reducer, composeWithDevTools(middleware));
index.jsx
import React from 'react';
import store from './store.js';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App.jsx';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { authenticateUser } from '../actions/authActions.js';
import Auth from '../components/Auth.jsx';
class App extends Component {
constructor(props) {
super(props);
this.state = {
authenticated: false // this needs to be set
};
}
componentWillMount() {
console.log('APP PROPS', this.props);
// this.props.actions.authenticateUser();
authenticateUser(); // this runs but doesn't run the dispatch function
// What I think needs to happen here Dispatch an Action and then setState referring back to how I would previous build with React Redux.
}
render() {
return (
<Auth app_name={ApplicationName} authenticated={this.state.authenticated} {...this.props} />
);
}
}
const mapStateToProps = state => {
console.log('redux store auth state', state);
return {
auth: state.auth
};
};
const mapDispatchToProps = dispatch => {
return { actions: bindActionCreators(authenticateUser, dispatch) };
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Auth.jsx
import React from 'react';
import { Route } from 'react-router-dom';
import AuthError from './AuthError.jsx';
import Layout from './Layout.jsx';
export default function Auth(props) {
console.log('AUTH PROPS', props);
const renderLayout = () => {
if (props.authenticated == true) {
return <Layout app_name={props.app_name} />;
} else {
return <AuthError />;
}
};
return <Route path="/" render={renderLayout} />;
}
authReducer.js
export default function reducer(
state = {
authenticated: null
},
action
) {
switch (action.type) {
case 'AUTH_SUCCESSFUL': {
return {
...state,
authenticated: action.payload.authenticated
};
break;
}
case 'AUTH_REJECTED': {
return {
...state,
authenticated: false
};
}
}
return state;
}
authActions.js
import axios from 'axios';
export function authenticateUser() {
console.log('authenticate user action has been called');
return function(dispatch) {
// nothing runs within this block so it's leading me to believe nothing is being `dispatch`ed
console.log('dispatch', dispatch);
axios
.get('localhost:3004/auth')
.then(response => {
dispatch({ type: 'AUTH_SUCCESSFUL', payload: response.data });
console.log('response', response);
})
.catch(err => {
dispatch({ type: 'AUTH_REJECTED', payload: err });
console.log('error', err);
});
};
}
Right now inside of App.jsx I can console the state of the authReducer and I can call authenticateUser() in my actions. But when I call authenticateUser() the return dispatch function doesn't run. Should I be dispatching the auth action in App.jsx? Or should I be dispatching the auth in Auth.jsx as a prop to then have App.jsx fetch the data? Just a bit lost on breaking this apart and what piece should be doing what work.
I'll do a brief explanation about it to help you to understand those patterns and don't get in confusion anymore (I hope).
So, let's forget reducers for a moment to focus on container, action creator and component pattern.
Component
A lot of people implement components by wrong way when using it with redux application.
A better component approach for redux is, implement it with stateless pattern (see Functional Components). Let's see in practice:
// components/Subscribe.js
import React from 'react'
import PropTypes from 'prop-types'
const Subscribe = ({text, confirmSubscription}) =>
<div>
<p>{text}</p>
<button onClick={confirmSubscription}>Confirm</button>
</div>
Subscribe.propTypes = {
subtitle: PropTypes.string.isRequired
}
Subscribe.defaultProps = {
subtitle: ''
}
export default Subtitle
This allows you to optimize component footprint because they have less features than stateful components (or class components), so you will win some performance and keep focused on component objective.
Container
In other hand, Container is a kind of component with some logical implementation. Container is a pattern created to bind React and Redux, because both should't interact directly. This means, a Container render the component, handle some component events (for example, form onSubmit) and feed components with application state. So, the Container is the best place to interact with Redux. (react-redux)[https://github.com/reactjs/react-redux] and Redux make this task a bit easier. So a simple Container to feed and capture interactions on Subscribe component could be like this:
// containers/SubscribeContainer.js
import React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { confirmSubscription } from 'actions/subscription'
import Subscribe from 'components/Subscribe'
const mapStateToProps = state => ({
text: state.subscription.text
})
const mapDispatchToProps = dispatch =>
bindActionCreators({
confirmSubscription
}, dispatch)
const Container = connect(mapStateToProps, mapDispatchToProps)
export default Container(Subscribe)
Action Creator
An action creator (or action creators), is just a collection of or a function where return an action. Simple like that:
// actions/subscription
export const CONFIRM_SUBSCRIPTION = 'actions.confirmSubscription'
export function confirmSubscription() {
return {
type: CONFIRM_SUBSCRIPTION
}
}
For now, we have the triad pattern, Component, Container and Action Creator implemented, from here, you just need two more things to make this working with Redux.
Create a subscription store.
Handle CONFIRM_SUBSCRIPTION (in case to update app's state)
Return a new state
The magic will happen when you return a new state from any reducer, the mapStateToProps will be called and you will receive the new state as argument and from there, React will update your components when necessary, in case of those components are stateless, PureComponent (works only with single level states and props) or custom shouldComponentUpdate.
Another thing to keep on mind is to not do fetch or async execution inside Components, Containers and Action Creators, instead, you can use middleware like redux-thunk to compose a custom middeware to capture actions and handle that before be sent to reducers.
your authenticateUser returns a function, you need to literally run the function. The right way to do that is to add a property in your mapDispatchToProps
const mapDispatchToProps = dispatch => {
return { authenticateUser: () => dispatch(authenticateUser()) };
};
Then, in your componentWillMount function, call
this.props.authenticateUer()
Check this

Resources