Re render Component when props updates (w/ Redux) - javascript

Ok so i have 2 components, Map and App. App component basically holds the map.
This is the code of my App:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { StackNavigator, TabNavigator } from 'react-navigation';
//Redux
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
//Screen Imports
import CenterNew from './screens/CenterNew';
import Map from './components/Map';
import Profile from './containers/Profile';
import Review from './containers/Review';
import { connect } from 'react-redux';
var store = createStore(rootReducer, {}, applyMiddleware(thunk));
class App extends Component {
render () {
{ console.ignoredYellowBox = ['Remote debugger']; }
const MainScreenNavigator = TabNavigator({
Centers: {screen: Map},
Review: {screen: Review},
Profile: {screen: Profile}
});
MainScreenNavigator.navigationOptions = {
title: this.props.map.title
};
const Screens = StackNavigator({
Home: {screen: MainScreenNavigator},
CenterNew: {screen: CenterNew}
});
return (
<Provider store={store}>
<Screens />
</Provider>
);
}
}
const connectWithStore = (store, WrappedComponent, mapStateToProps) => {
let ConnectedWrappedComponent = connect(mapStateToProps)(WrappedComponent);
return (props) => {
return <ConnectedWrappedComponent {...props} store={store} />
}
}
const mapStateToProps = (state) => {
return {
map: state.map
};
};
App = connectWithStore(createStore(rootReducer, {}, applyMiddleware(thunk)), App, mapStateToProps);
export default App;
In the snippet, i set the title using
MainScreenNavigator.navigationOptions = {
title: this.props.map.title
};
So whenever App is rendered, I log the initial state as you could see in componentWillMount() and this is the result. A syou can see, the initial title is 'Home'
now, in my Map I have a button which triggers this action creator just to change the header text:
this.props.changeHeaderTitle('Test');
console.log(this.props.map);
code:
export const changeHeaderTitle = (title) => {
return {
type: 'CHANGE_HEADER_TITLE',
payload: title
};
};
and this is my reducer:
const INITIAL_STATE = {
isPinnable: false,
pinnedCoordinateLatitude: 0,
pinnedCoordinateLongitude: 0,
region: {
latitude: 14.582524,
longitude: 121.061547,
latitudeDelta: 0.007,
longitudeDelta: 0.007
},
title: '',
searched: false
};
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case 'ENABLE_PINNING':
return { ... state, isPinnable: true }
case 'DISABLE_PINNING':
return { ... state, isPinnable: false,
pinnedCoordinateLatitude: action.payload.latitude,
pinnedCoordinateLongitude: action.payload.longitude }
case 'GET_PINNED_COORDINATE':
let test = action.payload.longitude;
return {
...state,
isPinnable: false,
pinnedCoordinateLatitude: action.payload.latitude,
pinnedCoordinateLongitude: action.payload.longitude
}
case 'GET_CURRENT_REGION':
return { ...state, region: region }
case 'CHANGE_CURRENT_REGION':
return { ...state, region: action.payload}
case 'CHANGE_HEADER_TITLE':
return { ...state, title: action.payload }
case 'SEARCHED_VIEW':
return { ...state, searched: action.payload }
default:
return state;
}
};
whenever the action is triggered, i know the title is updated because i log it as you can see in
this.props.changeHeaderTitle('Test');
console.log(this.props.map);
but in my view, the title is still 'Home'.. I referred to this: rerender react component when prop changes and tried to use componentWillRecieveProps() but it is not logging when this.props.changeHeaderTitle('Test'); action is triggered from Map component.

You need to connect your Redux store with your component. Therefore you should add this snippet:
mapStateToProps(store){
const { title } = store;
return { title };
}
export default connect(mapStateToProps, { changeHeaderTitle })(App)
The mapStateToProps function will subscribe to redux store updates. Every time your state changes, your component will rerender automatically. You can find more information about connecting components to the redux store here: react-redux api docs

You can't update the screen title like in the code you provide.
//This is only needed if you want to set the title from another screen
static navigationOptions = ({ navigation }) => {
const {state} = navigation;
return { title: `${state.params.title}`,
};
};
ChangeThisTitle = (titleText) => {
const {setParams} = this.props.navigation; setParams({ title: titleText })
}
//Your render method
render(){
//Views goes here
}
To change the title you have call the change title method from the onPress.
You may please refer this link for more details.

Related

React Redux updates state, but no re-renders, and props is undefined in render(){}

So i'm trying to setup a timer within a react-redux store. My action are being sent to the reducer, i can see it in the console if i console.log from the reducer.
I even have the props hooked up where the button to start time and stop timer works, as i see the "TICK" action every second in the console. However my biggest confusion at the moment is, even though i got it hooked up on the store i just can't seem to see the values render in my component. this.propsis undefined on componentDidMount and render(){}. however a button on that same component does <button onClick={this.props.startTimer}>Start</button> trigger the startTimer action, so it is updating state, so why is component not reflecting it and why is my this.props value undefined in the component? Oh yeah and just to add my other two redux reducer/actions work perfectly fine even though they're doing a fetch/promise using the thunk middleware. Any help/advice is welcome as this is my last hurdle on my app
Here is the code:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk';
import reducers from './reducers'
export const store = createStore(reducers, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
reducers
export default (state = [], action) => {
switch (action.type) {
case "FORECAST_WEATHER":
return action.payload;
default: return state;
}
};
export default (state = [], action) => {
switch (action.type) {
case "CURRENT_WEATHER":
return action.payload;
default: return state;
}
};
const START_TIMER = "START_TIMER";
const STOP_IT = "STOP_IT";
const RESUME_IT = "RESUME_IT";
const TICK = "TICK";
const initialState = {
timerOn: true,
timerStart: 0,
timerTime: 30
};
export default (state = initialState, action) => {
console.log("state is ", state);
console.log("action is ", action);
switch (action.type) {
case START_TIMER:
return {
...state,
timerOn: true,
timerTime: state.timerTime,
timerStart: Date.now() - state.timerTime,
timerId: action.timerId
};
case STOP_IT: {
return {
...state,
timerOn: false,
timerStart: 0
};
}
case TICK:
return { ...state, timerTime: state.timerTime + 1 };
default:
return state;
}
};
import { combineReducers } from "redux";
import currentWeatherReducer from './currentWeatherReducer';
import forecastReducer from './forecastReducer';
import currentTimeReducer from './currentTimeReducer';
export default combineReducers({currentWeather: currentWeatherReducer, forecast: forecastReducer, currentTime: currentTimeReducer});
actions
import { getForecast } from "../api/GET/getForecast";
import { getCurrentWeather } from "../api/GET/getCurrentWeather";
export const fetchCurrentWeather = () => async (dispatch, getState) => {
const { name, dt, main } = await getCurrentWeather();
const cityProperties = { name, dt, main };
dispatch({ type: "CURRENT_WEATHER", payload: cityProperties });
};
export const fetchForecasts = () => async (dispatch) => {
const { list } = await getForecast();
dispatch({ type: "FORECAST_WEATHER", payload: list });
};
timer component
import React, { useState, useEffect } from "react";
import "./Timer.scss";
import { connect } from "react-redux";
import { start, stop, tick } from "../../actions";
class Timer extends React.Component {
constructor(props) {
super(props);
}
handleStop = () => {
clearInterval(this.props.timerId);
this.props.stopTimer();
};
componentDidMount() {
console.log("TIMER PROPS IS UNDEFINED HERE", this.props);
}
render() {
const { timerTime } = this.props;
console.log("PROPS ARE ALSO UNDEFINED HERE", this.props);
return (
<div className="timer-container">
<button onClick={this.props.startTimer}>Start</button>
TIME IS: {timerTime}
<button onClick={this.handleStop}>Stop</button>
<button onClick={this.props.resetTimer}>Reset</button>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
timerOn: state.timerOn,
timerStart: state.timerStart,
timerTime: state.timerTime,
timerId: state.timerId,
};
};
const mapDispatchToProps = (dispatch) => {
console.log("dispatch", dispatch);
let timerId;
return {
startTimer: () => {
timerId = setInterval(() => dispatch({ type: "TICK" }), 1000);
dispatch({ type: "START_TIMER", timerId });
},
stopTimer: () => {
dispatch({ type: "STOP_IT" });
clearInterval(timerId);
},
resetTimer: () => dispatch({ type: "RESUME_IT" }),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Timer);
Other components connected to the store that works as expected
import React from "react";
import "./Header.scss";
import Timer from "../Timer";
import { connect } from "react-redux";
import { fetchCurrentWeather } from "../../actions";
class Header extends React.Component {
componentDidMount() {
this.props.fetchCurrentWeather();
console.log(this.props.currentWeather);
}
render() {
const { name, dt, temp } = this.props.currentWeather
return (
<div className="top-header">
<div className="current-city info">
<h1>{name}</h1>
<div className="time-container">
<i className="time-icon icon" >xxx</i>
<span className="time-text">{dt}</span>
<i className="time-icon icon" >xxx</i>
</div>
<span className="degrees">{temp}°</span>
</div>
<Timer />
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
return { currentWeather: state.currentWeather };
};
export default connect(mapStateToProps, { fetchCurrentWeather })(Header);
Another component connected to store that works
import React, { useState, useEffect } from "react";
import "./WeatherCard.scss";
import {fetchForecasts} from '../../actions';
import {connect} from 'react-redux';
class WeatherCard extends React.Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.props.fetchForecasts();
console.log('PROPS IS DEFINED HERE', this.props);
}
render() {
const allRelevantData = Object.entries(this.props.forecast).map(([key, value]) => {
const dateTime = new Date(value.dt * 1000);
const day = dateTime.toString().slice(0, 3);
const item = {
day: day,
temp: Math.round(value.main.temp),
weatherMetaData: value.weather[0],
};
return item;
});
const uniqueForecasts = Array.from(
new Set(allRelevantData.map((a) => a.day))
).map((day) => {
return allRelevantData.find((a) => a.day === day);
});
return uniqueForecasts.map(({ day, temp, weatherMetaData }, index) => {
if (index < 5) {
return (
<div className="weather-card" key={index}>
<div className="day-temperature-container">
<span className="day">{day}</span>
<span className="temperature fade-in">{temp}°</span>
</div>
<div className="weather-description">
<span
className="icon weather"
style={{
background: `url(http://openweathermap.org/img/wn/${weatherMetaData.icon}.png)`,
}}
/>
<p>{weatherMetaData.description}</p>
</div>
</div>
);
}
});
}
}
const mapStateToProps = (state, ownProps) => {
return {forecast: state.forecast};
};
export default connect(mapStateToProps, {fetchForecasts})(WeatherCard);
You need to pull off states from your state tree that is shaped with reducers. And you have three reducers.
In the Timer.js file you need to pull of states from the state shaped by currentTime reducer.
If you change mapStateToProps in Timer.js, as below; you can get the states:
Timer.js
...
//pulling of states from the state tree that shaped with currentTime reducer
const mapStateToProps = (state) => {
return {
timerOn: state.currentTime.timerOn,
timerStart: state.currentTime.timerStart,
timerTime: state.currentTime.timerTime,
timerId: state.currentTime.timerId,
};
};
Not related but i just wanted to promote to use redux-logger :)
reducer.js
...
//Create Store
// import { createLogger } from 'redux-logger';
const loggerMiddleware = createLogger();
const rootReducer = combineReducers({
currentWeather: currentWeatherReducer,
forecast: forecastReducer,
currentTime: currentTimeReducer
})
export const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
);
It sounds like you are having trouble pulling from the redux reducer state.
Normally I will,
In my component I will set a useEffect() or componentDidMount that will initiate my setInterval() function. The set Interval function will be ran every 1000ms if your timing by seconds.
2.) In my setInterval function, I will dispatch an action to increment the reducer's initial state value of 0++ (currentValue++) during each iteration of the interval.
3.) In my component, I will pass the props to the component. Be sure to check if the values need to be either mapped or directly passed in without a .map function.
I also recomend using Moment.js. You can do all of this easily with that library.

Redux store property gets nested into itself on update

I'm having this issue where my props are ending up looking like this (on console.log):
{
fetchRoles: f(),
roles:
roles: ["Admin", "Manager"],
}
As you can see, somewhere in my code I'm making a mistake that causes the roles prop to get nested into itself, which would force me into doing const { roles } = this.props.roles; in order to retrieve my data (which works BTW).
I've looked around for help but not many people seem to have run into this issue (I'm just getting started with redux).
Below you can see my files:
rolesReducer.js:
import { FETCH_ROLES } from "../actions/types";
const initialState = {
roles: [],
};
export default function (state = initialState, action) {
const { roles } = action;
switch (action.type) {
case FETCH_ROLES:
return {
...state, //also tried ...state.roles and same issue.
roles,
};
default:
return state;
}
}
rolesActions.js:
import { FETCH_ROLES } from "./types";
const roles = ["SuperAdmin"];
export function fetchRoles() {
return function (dispatch) {
dispatch({
type: FETCH_ROLES,
roles,
});
};
}
reducers/index.js (root reducer):
import { combineReducers } from "redux";
import rolesReducer from "./rolesReducer";
import roleMembersReducer from "./roleMembersReducer";
export default combineReducers({
roles: rolesReducer,
roleMembers: roleMembersReducer,
});
PermissionsManager.jsx:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Container } from "react-bootstrap";
import { fetchRoles } from "../redux/actions/rolesActions";
import RoleContainer from "./RoleContainer";
class PermissionsManager extends Component {
componentDidMount() {
this.props.fetchRoles();
}
render() {
console.log(this.props);
const { roles } = this.props.roles;
return (
<Container>
{roles.map((role) => {
return <RoleContainer key={role} role={role} />;
})}
</Container>
);
}
}
const mapStateToProps = (state) => {
return {
roles: state.roles,
};
};
export default connect(mapStateToProps, { fetchRoles })(PermissionsManager);
Edit 1 - Adding reducer log:
As suggested, I logged the reducer, specifically state and action:
state:
{
roles: [],
}
action:
{
roles: ["Admin", "Manager"],
type: "FETCH_ROLES",
}
No duplication or abnormal structures I believe.
One way to shape the store like you're asking is to flatten roles in rolesReducer.js,
you can do so storing the received array directly in the partial state:
initialState would need to look like
const initialState = []
and in the switch statement
case FETCH_ROLES:
return roles

React-Native Redux connected component not dispatching actions

I have connected my component to redux, defined mapStateToProps and mapDispatchToProps and still dispatch doesn't seem to be doing anything.
When I run this, 'console.log("Setting token")' is printed out, but nothing else happens. So this.props.setToken is firing, because that's where that console.log is set, but the store isn't updating and this.props.auth_token isn't showing on the screen like it should if dispatch(setToken) had fired correctly...
AppScreen
import React from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
import { connect } from 'react-redux';
import { setToken } from './reducer';
class AppScreen extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.setToken("token_abc");
}
render() {
if (this.props.loading) {
return (
<View style={{ flex: 1 }}>
<ActivityIndicator />
</View>
)
} else {
return (
<View>
<Text>You're in! '{this.props.auth_token}'</Text>
</View>
)
}
}
}
function mapStateToProps(state) {
return {
user: state.user,
auth_token: state.auth_token,
loading: state.loading,
error: state.error
};
}
const mapDispatchToProps = dispatch => {
return {
setToken: token => {
console.log("Setting token")
dispatch(setToken(token));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AppScreen);
Reducer
import { createSlice } from "#reduxjs/toolkit";
const appSlice = createSlice({
name: "app",
initialState: {
loading: true,
auth_token: "",
error: "",
user: {}
},
reducers: {
setToken: (state, action) => {
state.auth_token = action.payload;
state.loading = false;
},
},
extraReducers: {
}
});
export const { setToken } = appSlice.actions;
export const appReducer = appSlice.reducer;
Store
import { appReducer } from "./App/reducer";
import { configureStore, getDefaultMiddleware } from "#reduxjs/toolkit";
const middleware = [
...getDefaultMiddleware(),
]
const store = configureStore({
reducer: {
app: appReducer
},
middleware,
});
export default store;
You've got a mismatch between the combined store reducer setup, and what your mapState is trying to do.
Your component expects that state.auth_token will exist. The appSlice.reducer has an auth_token field inside. Therefore, state.auth_token will only exist if appSlice.reducer is the root reducer for the entire store.
However, you're currently passing appSlice.reducer as a "slice reducer" for state.app, which will result in state.app.auth_token.
You need to either pass reducer: appReducer to make it be the root reducer by itself, or update your mapState to read state.app.auth_token.
Given that you're likely going to have other slices, I'd recommend the latter approach.

React-Redux: How do I populate a component's prop on load with data from an async call?

I have this autocomplete component that takes an array of terms as a dataSource prop. The data I want to feed in resides in a public API, and I've followed the tutorial here to get to the code below. But this tutorial (and many others out there) explain how to bind these actions to an event, whereas I want to populate this prop with data on page load. How would I go about doing that?
actions.js
import fetch from 'isomorphic-fetch';
export function loadSchools(termId) {
return {
type: 'LOAD_SCHOOLS',
termId
};
}
export function receiveSchools(termId, json) {
return {
type: 'RECEIVE_SCHOOLS',
termId,
schools: json.data.children.map(child => child.data), // ???
receivedAt: Date.now()
};
}
export function getSchools(termId) {
return function (dispatch) {
dispatch(loadSchools(termId));
return fetch('http://www.northwestern.edu/class-descriptions/4650/index-v2.json')
.then(response => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => dispatch(receiveSchools(termId, data)));
};
}
reducers.js
const initialState = {
schoolsData: {
isFetching: false,
lastUpdated: 0,
schools: []
}
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
export default schools;
Search.jsx
import React from 'react';
import AutoComplete from 'material-ui/AutoComplete';
export default class Search extends React.Component {
render() {
return (
<AutoComplete
hintText="Search for something."
dataSource={this.props.searchdata}
maxSearchResults={15}
filter={AutoComplete.caseInsensitiveFilter}
onNewRequest={}
/>
);
}
}
Search.propTypes = {
searchdata: React.PropTypes.array.isRequired,
onSelect: React.PropTypes.func
};
index.jsx
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { grey500, white, fullBlack } from 'material-ui/styles/colors';
import { fade } from 'material-ui/utils/colorManipulator';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import schools from './reducers/reducers';
import colors from './colors';
import NavBar from './components/NavBar.jsx';
import Serif from './components/Serif.jsx';
const store = createStore(schools, applyMiddleware(thunkMiddleware));
const muiTheme = getMuiTheme({
palette: {
primary1Color: colors.northwesternPurple,
primary2Color: colors.northwesternPurple120,
primary3Color: grey500,
accent1Color: colors.northwesternPurple30,
accent2Color: colors.richBlack10,
accent3Color: colors.richBlack50,
textColor: colors.richBlack80,
alternateTextColor: white,
canvasColor: white,
borderColor: colors.richBlack20,
disabledColor: fade(colors.richBlack80, 0.3),
pickerHeaderColor: colors.northwesternPurple,
clockCircleColor: fade(colors.richBlack80, 0.07),
shadowColor: fullBlack
}
});
class App extends React.Component {
render() {
return (
<Provider store={store}>
<MuiThemeProvider muiTheme={muiTheme}>
<div> {/* MuiThemeProvider requires stricly one child element */}
<NavBar />
<Serif /> {/* This component contains SearchContainer, which in turn contains Search */}
</div>
</MuiThemeProvider>
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
You can render your Search component from another component, let's call it SearchContainer. SearchContainer is decorated by the connect function from react-redux that has as only role to dispatch the action to fetch the schools. SearchContainer doesn't render Search component until the school are fetched.
Here an example of what the code would look like. Here I assume you don't use react-redux.
First you have a small problem in your initial state in reducers.js. It should be:
const initialState = {
isFetching: false,
lastUpdated: 0,
schools: []
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
SearchContainer.js
// ./containers/SearchContainer.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadSchools } from '../actions/actions'
import Search from '../components/Search';
class SearchContainer extends Component {
componentDidMount() {
this.props.loadSchools(this.props.termId);
},
render() {
const {
schools,
isFetching
} = this.props;
if (isFetching) {
return null;
}
return <Search schools={schools} />;
}
}
const mapStateToProps = (state) => ({
isFetching: state.isFetching,
schools: state.schools
});
const mapActionsToProps = (dispatch) => ({
loadSchools: (termId) => dispatch(loadSchools(termId)),
});
export default connect(mapStateToProps, mapActionsToProps)(SearchContainer);
In this way, at the first render, your Search component is not rendered. It is rendered, only after the schools are loaded.
You can dispatch the LOAD_SCHOOLS action from the componentDidMount lifecycle method (maybe in your Serif component but I can't see the code for that).
From the docs:
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
https://facebook.github.io/react/docs/react-component.html#componentdidmount

React redux display data from async action

I am building a react app and implementing redux for data. When I navigate to a particular route, I want to dispatch the action to fetch the data from an external API and then once the data comes back, display the data for the user.
Store :
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';
import rootReducer from '../reducers/index';
const initialState = {
marvel :{
characters: []
}
};
const store = createStore(rootReducer, initialState, applyMiddleware(thunkMiddleware, createLogger()));
export const history = syncHistoryWithStore(browserHistory, store);
if (module.hot) {
module.hot.accept('../reducers/', () => {
const nextRootReducer = require('../reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}
export default store;
reducer :
import * as constants from '../constants/constants';
const initialState = {
characters: [],
isFetching: false,
errorMessage: null
};
const marvelReducer = (state = initialState, action) => {
switch (action.type) {
case constants.FETCH_MARVEL :
return Object.assign({},state,{isFetching: true});
case constants.FETCH_MARVEL_SUCCESS :
return Object.assign({}. state,{
characters: [...action.response],
isFetching: false
});
case constants.FETCH_MARVEL_ERROR :
return Object.assign({}, state,{
isFetching: false,
errorMessage: action.message
});
default :
return state;
}
};
export default marvelReducer;
actions:
import 'whatwg-fetch';
import * as constants from '../constants/constants';
export const fetchMarvel = (dispatch) => {
const MARVEL_API = 'http://gateway.marvel.com:80/v1/public/characters?apikey=e542b1d89f93ed41b132eda89b9efb2c';
dispatch({
type: constants.FETCH_MARVEL
});
return fetch(MARVEL_API).then(
response => {
dispatch({
type: constants.FETCH_MARVEL_SUCCESS,
response
});
},
error => {
dispatch({
type: constants.FETCH_MARVEL_ERROR,
message: error.message || 'Something went wrong with fetchMarvel'
});
});
};
component :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/actions';
import '../styles/homeStyles.css';
class Home extends Component {
render() {
const { characters, isFetching, errorMessage } = this.props;
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}
function mapStateToProps(state) {
return {
characters: state.characters,
isFetching: state.isFetching,
errorMessage: state.errorMessage
};
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actions, dispatch) };
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
I know I'm not currently displaying the props anywhere in the application, but first I am just trying to get them populated.
What step am I missing to dispatch the action so that I can populate the props from state?
You are not dispatching the action anywhere. So nothing happens.
You probably want to do this in a React lifecycle hook, for example:
class Home extends Component {
componentDidMount() {
this.props.actions.fetchMarvel();
}
render() {
const { characters, isFetching, errorMessage } = this.props;
return (
<div>
<h1>React Starter App</h1>
<h2>This is the home page</h2>
</div>
);
}
}

Categories

Resources