Dispatch is not available in this.props - reactjs

I'm very new to React and trying to write an application which outputs a portfolio to one part of the page and, based on user interaction with that portfolio, displays some information in a lightbox/modal elsewhere in the DOM.
This requires that my two rendered components have some kind of shared state, and my understanding is that the best (or one of the best) way to achieve this is with Redux. However, being new to React and now adding Redux into the mix, I'm a little out of my depth.
I've created some (for now very dumb) action creators and reducers, all I'm trying to do initially is fetch some JSON and add it to my store. However, I'm not able to access dispatch from within my component and I'm not really sure where I'm going wrong.
If I console.log this.props from within my component I get an empty object, "{}".
Here are the main parts, any pointers would be really appreciated:
App.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import store from './redux/store';
import { Portfolio } from './redux/components/portfolio';
ReactDOM.render(
<Provider store={store}>
<Portfolio />
</Provider>,
document.getElementById('portfolioCollection')
);
actions/actionCreators.js:
export const populatePortfolio = obj => ({
type: POPULATE_PORTFOLIO,
obj
});
export const populateLightbox = obj => ({
type: POPULATE_LIGHTBOX,
obj
});
portfolio.js:
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
export class Portfolio extends React.Component {
componentDidMount() {
this.getPortfolioData();
}
getPortfolioData() {
fetch('/data.json')
.then( (response) => {
return response.json()
})
.then( (json) => {
// dispatch action to update portfolio here
});
}
render() {
return(
// render component
);
}
}
function mapStateToProps(state){
console.log('state', state);
return {
state: state
}
};
function mapDispatchToProps(dispatch) {
console.log('dispatch', dispatch);
return {
actions: bindActionCreators({ populatePortfolio: populatePortfolio }, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Portfolio);

this.props is empty because you have not passed any props. You are using the unconnected component instead of the one that has been connected to redux.
To fix this, replace this line:
import { Portfolio } from './redux/components/portfolio';
with
import Portfolio from './redux/components/portfolio';
You are exporting both the connected and the unconnected component. You probably only want the last export. Since the connected component is exported as default you import it without using {} deconstruction.
unless you need to import the unconnected component in tests or something like that, you can remove the export statement from this line, since it makes no sense to export something that you don't intend to import in another file.
export class Portfolio extends React.Component {

You aren't meant to manually call dispatch in your components. The action creator function is automatically bound to dispatch for you. Simply call this.props.populatePortfolio() in your component.

Related

React Connected Component package without host having redux

I'm new to react and I'm trying to create a connected component (redux) for internal use in the company but I don't want to force the host app to have redux. I want to avoid having my component used like this:
<Provider store={store}><MyComponent /></Provider>
I thought in using something like Higher-Order Components and Contexts, but it is just not clicking.
Even creating my own provider would be acceptable, something like:
<MyComponentProvider><MyComponent /></MyComponentProvider>
Is it possible for a host application to use a Redux Component without having redux as a dependency?
It should be possible. The component can have its own redux store. Keep it as a complete black box.
Here is a demo, consider the package directory is your npm package and we install the redux, react-redux packages in the npm package, not your host or main project.
package/combobox.tsx:
import * as React from 'react';
import { useSelector } from 'react-redux';
export const Combobox = () => {
const state = useSelector((state) => state);
console.log('state: ', state);
return <div>combobox</div>;
};
we can wrap it in a special component that initializes the store in the constructor:
package/index.tsx:
import { Component } from 'react';
import * as React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { Combobox } from './combobox';
const reducer = (state = { counter: 1 }) => {
return state;
};
export default class ComboboxConnected extends Component {
store: ReturnType<typeof createStore>;
constructor(props) {
super(props);
this.store = createStore(reducer);
}
render() {
return (
<Provider store={this.store}>
<Combobox />
</Provider>
);
}
}
Now, each Combobox component instance has its own redux store and state.
Consumer side
App.tsx:
import * as React from 'react';
import Combobox from './package/index';
import './style.css';
export default function App() {
return (
<div>
<Combobox />
</div>
);
}
See Isolating Redux Sub-Apps
stackblitz

console.log(this.props.store) returning undefined in my create react app?

I created a create react app and included redux with card lists and a searchbox that displayed the filtered results, the app was working before I added redux but now it isn't returning any results. When I console.log(this.props.store) it is returning undefined. I would really appreciate it if someone can help me with this. My files are as below:
constants.js
export const CHANGE_SEARCH_FIELD = 'CHANGE_SEARCH_FIELD';
actions.js
import {CHANGE_SEARCH_FIELD} from './constants.js';
export const setSearchField = (text) => ({
type: CHANGE_SEARCH_FIELD,
payload: text
})
reducer.js
import {CHANGE_SEARCH_FIELD} from './constants.js';
const intialState = {
searchField: ''
}
export const searchTeacher = (state=intialState, action={}) => {
switch(action.type) {
case CHANGE_SEARCH_FIELD:
return Object.assign({}, state, { searchField: action.payload });
default:
return state;
}
}
index.js
import ReactDOM from 'react-dom';
import './index.css';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import App from './App.js'; //Our main parent component
import {searchTeacher} from './reducer.js';
import 'tachyons';
import * as serviceWorker from './serviceWorker';
const store = createStore(searchTeacher)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root') );
serviceWorker.unregister();
App.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import CardList from './CardList.js';
import {teacher} from './teacher.js';
import Searchbox from './searchbox.js';
import ErrorBoundry from './ErrorBoundry';
import Scroll from './Scroll.js';
import './App.css';
import {setSearchField} from './actions.js';
const mapStateToProps = state => {
return {
searchField: state.searchField
}
}
const mapDispatchToProps = (dispatch) => {
return {
onSearchChange: (event) => dispatch(setSearchField(event.target.value))
}
}
class App extends Component {
constructor(){
super()
this.state = {
teacher: teacher, //teacher: [],
}
}
render(){
console.log(this.props.store);
const { searchField, onSearchchange } = this.props;
const filteredteacher= teacher.filter(
teacher =>{
return teacher.name.toLowerCase().includes(searchField.toLowerCase());
});
return(
<div className="tc">
<h1 className="f1"> Faculty Members ! </h1>
<Searchbox searchChange={onSearchchange} />
<Scroll>
<ErrorBoundry>
<CardList teacher={filteredteacher} />
</ErrorBoundry>
</Scroll>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
There won't be any props.store, because none of your code is passing down a prop named store to that component.
Components that have been wrapped in connect get props from three sources, combined:
Props passed from the parent component
Props returned from mapState
Props returned from mapDispatch
In this case, mapState is returning {searchField}, and mapDispatch is returning {onSearchChange}, and there's no props from the parent. So, the combined props are {searchField, onSearchChange}.
As a side note, you should use the "object shorthand" form of mapDispatch instead of writing it as a function:
const mapDispatch = {onSearchChange: setSearchField};
You will get two props from redux according to your code,
this.props.searchField
this.props.onSearchChange
connect function of react-redux used to connect react and redux.
mapDispatch is used to dispatch your actions which hold the payload(Second argument of connect function)
mapState is used to get the state of your properties(First argument of connect function)
So in your code, there is not any prop named store, Store is a global redux state which you can get with this method Store.getState() but here is store is redux store which you are passing here const store = createStore(searchTeacher) in your index.js file, This will show whole state of the redux store.
here is how you can get the state of your store.
How do I access store state in React Redux?
You will dispatch an action named onSearchChange like below in your on change method.
this.props.onSearchChange(e)
and redux will return you a value of this after storing in reducer with the name of this.props.searchField.
this.props.store would only be accessible if it was passed down from a parent component (which you are not doing here)
You create your store in index.js but you are not exposing an interface to it.
const store = createStore(searchTeacher);
You can expose these functions from your index.js file to reference the store:
export const getStore = () => store;
export const getState = () => { return store.getState(); };
Then from anywhere else (although not good practice):
import { getStore, getState } from 'index.js';

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

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

Wondering where to house api call logic. inside the Container or Component?

I have a container that passes props and an apiCall action to a component which will mainly just render the result of that call. My question is should I leave the invoking of that action up to the component or move it out into the container and just pass the array of items to the component?
Here is my container code. The fetchShowingsListShowings is the one in question. Also, I will be renaming that soon enough so bear with me.
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as actions from '../actions/showingsListActions';
import ShowingsList from '../components/ShowingsList';
const ShowingsListContainer = (props) => {
return (
<ShowingsList
isLoading={props.isLoading}
showings={props.showings}
fetchShowingsListShowings={props.actions.fetchShowingsListShowings}
/>
);
};
ShowingsListContainer.propTypes = {
isLoading: PropTypes.bool.isRequired,
showings: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
const mapStateToProps = (state) => {
return {
isLoading: state.showingsList.isLoading,
showings: state.showingsList.showings
};
};
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ShowingsListContainer);
And my component. Which calls the API action on componentWillMount.
import React, { PropTypes } from 'react';
import ShowingsListItem from './ShowingsListItem';
class ShowingsList extends React.Component {
componentWillMount() {
this.props.fetchShowingsListShowings();
}
render() {
return (
this.props.isLoading ? <h1>Loading...</h1> :
<ul className="list-unstyled">
{this.props.showings.map((showing,index) => <ShowingsListItem showing={showing} key={'showing' + index}/>)}
</ul>
);
}
}
ShowingsList.propTypes = {
isLoading: PropTypes.bool.isRequired,
showings: PropTypes.array.isRequired,
fetchShowingsListShowings: PropTypes.func.isRequired
};
export default ShowingsList;
Thanks in advance.
So in React with Redux the term 'Container' just means a component that is connected to the Store, essentially whatever you use the react-redux 'connect' method with. Your ShowingsList can be a 'dumb' (or functional) component meaning it's just a component that takes in data and displays content. The general 'best' practice is to have your dumb components just be concerned with presentation, and your container components handle all the logic interacting with the Redux Store. If you follow this logic, fetch the data in the container, and pass the data to the nested component. That being said, it'll work either way so you don't really need to change anything if you're happy with it now.
To follow this pattern do something like this:
modify your Container component to be an ES6 class extends React.Component.. and optionally change your ShowingsList to be a functional component (like your ShowingsList is now)
put a componentWillMount in your Container and put the API call there.
pass the list to the presentational component.
Here's an article written by Dan Abramov, the author of Redux on this very topic.
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.g695y2gwd

Resources