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
Related
this view doesn't work with redux, i need use redux dispatch inside react class component, but i get error
"Invalid hook call. Hooks can only be called inside of the body of a
function component. This could happen for one of the following
reasons:\n1. You might have mismatching versions of React and the
renderer (such as React DOM)\n2. You might be breaking the Rules of
Hooks\n3. You might have more than one copy of React in the same
app\nSee https://reactjs.org/link/invalid-hook-call for tips about how
to debug and fix this problem."
inspectionView.jsx
import React from "react";
import {
Text,
View,
} from "react-native";
import { useSelector, useDispatch } from 'react-redux';
import {
incrementByAmount,
selectCount
} from '../redux/slice.js';
export default class InspectionList extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
dispatch = useDispatch();
count = useSelector(selectCount);
render() {
return (
<View >
<Text>inspection view</Text>
<button
onClick={() => dispatch(incrementByAmount("hello world"))}
>
Add Amount
</button>
<Text>{{count}}</Text>
</View>
);
}
}
Slice.js
import { createSlice } from '#reduxjs/toolkit'
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
incrementByAmount: (state, action) => {
state.value = action.payload
},
},
})
// Action creators are generated for each case reducer function
export const {incrementByAmount } = counterSlice.actions
export const selectCount = (state) => state.counter.value;
export default counterSlice.reducer
store.js
import { configureStore } from '#reduxjs/toolkit'
import counterReducer from './slice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
The error is correct. You can never call any hook inside of a class component, regardless of whether it's a React-Redux hook, a React built-in hook, or any other hook. Only function components can call hooks.
My main advice here would be to write function components instead. There's no good reason to be writing class components today.
See the new React beta docs to learn how to use function components and hooks:
https://beta.reactjs.org
If for some reason you must use class components (and the odds are you don't need to), you can still use the older React-Redux connect API:
https://react-redux.js.org/tutorials/connect
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';
I am creating a page in React. Lets say for eg. "Conatct us" page. This whole component must be reusable. So that other teams can use it as it is. This component will have its own redux store and api calls using axios.
What I want to confirm that if I export this "Contact Us" module as npm package, will it work fine for other teams? Why I am asking this is because other teams project will have their own redux store and axios instance. And I think we can have only one redux store in an app and maybe one axios interceptors (I may be wrong about axios though)
Could anyone help me out, what can be done in this case? One thing is sure that I will have to export this whole component as npm package.
I'm going to answer here to give you more details:
Let's say your component looks like this:
AboutUs:
import React, { Component } from "react";
import PropTypes from "prop-types";
export class AboutUs extends Component {
componentDidMount() {
const { fetchData } = this.props;
fetchData();
}
render() {
const { data, loading, error } = this.props;
if (loading) return <p>Loading</p>;
if (error) return <p>{error}</p>;
return (
// whatever you want to do with the data prop that comes from the fetch.
)
}
}
AboutUs.defaultProps = {
error: null,
};
// Here you declare what is needed for your component to work.
AboutUs.propTypes = {
error: PropTypes.string,
data: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
}),
fetchData: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
};
This component just takes a few props in order to work and the fetchData function will be a dispatch of any redux action.
So in one of the apps that are going to use the component library, assuming that they have their own store, you could do something like this.
In the component where you're planning to use the AboutUs component.
import React from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
// this is the action that performs the data fetching flow.
import { fetchAboutUs } from "redux-modules/aboutUs/actions";
// The component that is above
import { AboutUs } from "your-component-library";
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
fetchData: fetchDashboard,
},
dispatch
);
};
const mapStateToProps = state => ({
loading: state.aboutUsReducer.loading,
error: state.aboutUsReducer.error,
data: state.aboutUsReducer.data,
});
const ReduxAboutUs = connect(
mapStateToProps,
mapDispatchToProps
)(AboutUs);
// Use your connected redux component in the app.
const SampleComponent = () => {
return <ReduxAboutUs />
}
This ensures that your component can work out of the box without redux, because you can explicitly use it without the redux dependency and just pass regular props and it will continue working. Also if you have different applications where you are going to use it you will have the control of which part of the store you want to use to inject the props for this component. Proptypes are quite useful here, because we're enforcing a few props in order let the devs what do we need to pass in order for the component to work properly.
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'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.