I am building a React Native app and I am using Redux. Redux Persist is working fine but when I add Redux Thunk, the store's state is not persisted. The value of the state.test variable does change to the one fetched from the api but when I restart the app the value is again equal to the initial value. I have created a simple example to reproduce this behaviour. The example consists of 5 files and I have also created a git repository with the sample code https://github.com/JenteBeerten/reduxPersistProblem
Store/Actions/test.js
export const SET_TEST = 'SET_TEST';
export const fetchTest = () => {
return async dispatch => {
fetch('http://dummy.restapiexample.com/api/v1/employee/1',{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}).then((response) => response.json())
.then((json) => {
dispatch({
type: SET_TEST, test: json.data.employee_name
});
})
.catch((error) => {
console.error(error);
});
}
};
export const setTest = (test) => {
return { type: SET_TEST, test: test };
};
Store/reducers/test.js
import { SET_TEST } from "../actions/test";
const initialState = {
test: 'testInitialValue'
}
const testReducer = (state = initialState, action) => {
switch (action.type) {
case SET_TEST:
state.test = action.test;
return state;
default:
return state;
}
}
export default testReducer;
Store/configureStore.js
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import AsyncStorage from '#react-native-community/async-storage';
import ReduxThunk from 'redux-thunk';
import testReducer from './reducers/test';
const rootReducer = combineReducers({
test: testReducer
});
const persistConfig = {
key: 'root',
storage: AsyncStorage
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(
persistedReducer,
applyMiddleware(ReduxThunk)
);
let persistor = persistStore(store)
return { store, persistor }
}
App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import returnStoreAndPersistor from './store/configureStore';
import TestComponent from './TestComponent';
const {store, persistor} = returnStoreAndPersistor();
export default function App() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<View style={styles.container}>
<TestComponent></TestComponent>
</View>
</PersistGate>
</Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
TestComponent.js
import React, { useEffect } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import * as testActions from './store/actions/test';
const TestComponent = props => {
const dispatch = useDispatch();
var test = useSelector(state => state.test.test);
console.log('test='+test);
useEffect(() => {
dispatch(testActions.fetchTest());
}, [dispatch]);
return (
<View style={styles.container}>
<Text> Test is {test} </Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}
});
export default TestComponent;
In reducer you have to return copy of previous state as
import { SET_TEST } from "../actions/test";
const initialState = {
test: 'testInitialValue'
}
const testReducer = (state = initialState, action) => {
switch (action.type) {
case SET_TEST:
return {
...state,
test: action.test;
}
default:
return state;
}
}
export default testReducer;
Now it will persist.
thanks
Related
I had faced with a problem. The problem: I had tried to use context inside a middleware, but i`dont know how i can do it, because we can use useContext only in... 'Hooks can only be called inside of the body of a function component. Is it possible to use context inside the middleware? Thx for help!
'I have a context:
import { createContext, useState } from "react";
export const PopupContext = createContext();
export const PopupContextProvider = (props) => {
const [isShow, setIsShow] = useState(false);
return (<PopupContext.Provider
value={[isShow, setIsShow]}
>
{props.children}
</PopupContext.Provider>
)
}
my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './redux/store';
import { PopupContextProvider } from './context/popup/popup';
ReactDOM.render(
<React.StrictMode>
<PopupContextProvider>
<Provider store={store}>
<App />
</Provider>
</PopupContextProvider>
</React.StrictMode>,
document.getElementById('root')
);
and my store.js - it`s a Redux store, you know...
import { configureStore } from '#reduxjs/toolkit';
import alertMiddleware from '../middleware/alert.middleware';
import authReducer from './features/auth/auth-slice';
import cardReducer from './features/card/cardSlice';
const store = configureStore({
reducer: {
auth: authReducer,
card: cardReducer
},
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(alertMiddleware)
})
export default store;
and my dumb middleware
import { PopupContext } from '../context/popup/popup';
import { useContext, useState } from 'react';
const alertMiddleware = store => next => action => {
const isShow = useContext(PopupContext);
if (action.type === 'auth/login/rejected') {
console.log(isShow);
}
console.log('middleware');
next(action)
}
export default alertMiddleware;
It's not. Hooks cant be used outside of components. You should send isShow as an action payload when you dispatch the action. Then you would have something like in your middleware
...
if (action.type === 'auth/login/rejected') {
console.log(action.payload.isShow);
}
...
Well, like i had understood, it's imposible to use context in your middleware (so sad). In this way, i had created a slice with reducers and etc. And now, a can take out all my logic in reducers, if someone doesn't know what is it... well, it's like a global state with services, which are available from all points in your application. The logic is: when some action type happen, the middleware handles it and dispatches some actions, in this action you can do everething, in my case i change the state and read this state from my functional component. I belive, what i had done a good explain.
Sequence of actions: some action => middleware => process action in reducer => change state
My middleware:
import { showPopup } from '../features/popup/popup-slice';
const POPUP_PROPERTIES = {
loginRejected: {
message: "LOGIN ERROR",
styles: {
color: "white",
backgroundColor: "red"
}
},
invalidateLoggedInUser: {
message: "You are logged out from your account",
styles: {
color: "white",
backgroundColor: "#4BE066"
}
},
cardCreateFulfilled: {
message: "Card set successfully created",
styles: {
color: "white",
backgroundColor: "#01C9F7"
}
},
cardDeleteFulfilled: {
message: "Card set successfully deleted",
styles: {
color: "white",
backgroundColor: "#4BE066"
}
}
}
const PopupMiddleware = ({ dispatch, getState }) => next => action => {
const { type } = action;
switch (type) {
case 'auth/login/rejected': {
dispatch(showPopup(POPUP_PROPERTIES.loginRejected));
break;
}
case 'auth/invalidateLoggedInUser': {
dispatch(showPopup(POPUP_PROPERTIES.invalidateLoggedInUser));
break;
}
case 'card/create/fulfilled': {
dispatch(showPopup(POPUP_PROPERTIES.cardCreateFulfilled));
break;
}
case 'card/delete/fulfilled': {
dispatch(showPopup(POPUP_PROPERTIES.cardDeleteFulfilled));
break;
}
default: break;
}
next(action);
}
export default PopupMiddleware;
My slice
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
// -------------------------------------- Slice --------------------------------------
const initialState = {
popupEntity: {
message: null,
color: null,
},
isVisible: false
}
const popupSlice = createSlice({
name: 'popup',
initialState,
reducers: {
showPopup: {
reducer(state, action) {
state.popupEntity = action.payload;
state.isVisible = true;
}
},
hidePopup: {
reducer(state) {
state.isVisible = false;
}
}
},
})
export const { showPopup, hidePopup } = popupSlice.actions;
export default popupSlice.reducer;
// -------------------------------------- Selectors --------------------------------------
export const popupStateSelector = state => state.popup;
export const isVisibleSelector = state => state.popup.isVisible;
and store:
import { configureStore } from '#reduxjs/toolkit';
import PopupMiddleware from './middleware/popup.middleware';
import authReducer from './features/auth/auth-slice';
import cardReducer from './features/card/cardSlice';
import popupReducer from './features/popup/popup-slice';
const store = configureStore({
reducer: {
auth: authReducer,
card: cardReducer,
popup: popupReducer
},
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(PopupMiddleware)
})
export default store;
i want to start with redux and the connect function. When i use the useSelector hook, everything is fine, so my redux-state works. But with connect and the maptStateToProps function not. The number variable is undefined.
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import { connect } from 'react-redux'
import { incrementNumber } from '../actions/exampleAction'
const Incrementor = ({ number, increment }) => {
return (
<View>
<Text onPress={increment}>{number}</Text>
</View>
)
}
export default connect(mapStateToProps, mapDispatchToProps)(Incrementor)
const styles = StyleSheet.create({})
const mapStateToProps = (state) => {
return {
number: state.exampleReducer.number,
}
};
const mapDispatchToProps = (dispatch) => {
return {
increment: () => dispatch(incrementNumber()),
}
};
Here is the working solution with react Hooks:
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import { useSelector , useDispatch} from 'react-redux'
import { connect } from 'react-redux'
import { incrementNumber } from '../actions/exampleAction'
const Incrementor = () => {
const dispatch = useDispatch();
const number = useSelector(state => state.exampleReducer.number)
return (
<View>
<Text onPress={() => {dispatch({type: "INCREMENT"})}}>{number}</Text>
</View>
)
}
const styles = StyleSheet.create({})
export default Incrementor
Here is my reducer function:
import { INCREMENT } from "../actions/exampleAction";
let initialState = { number: 0 };
export default function reducer (state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, number: state.number+1, };
default:
return state;
}
}
And here the action:
export const INCREMENT = "INCREMENT";
export const incrementNumber = () => {
return {
type: INCREMENT,
}
};
Here i combine my reducers:
import { combineReducers } from 'redux'
import exampleReducer from "./exampleReducer"
const reducers = combineReducers({
exampleReducer,
});
export default reducers;
And here is my App.js
import React from 'react';
import { StyleSheet, View } from 'react-native'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux';
import Incrementor from './src/components/Incrementor';
import reducer from "./src/reducers/index"
let store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
export default function App() {
return (
<Provider store={store}>
<View style={styles.container}>
<Incrementor />
</View>
</Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
first of all, check you've imported incrementNumber() from actions, after that you have to use connect function after defining all mapStateToProps & mapDispatchToProps, your code will be like this
const mapStateToProps = (state) => ({
number: state
})
const mapDispatchToProps = (dispatch) => ({
increment: () => dispatch(incrementNumber())
})
export default connect(mapStateToProps, mapDispatchToProps)(Incrementor)
I'm struggling to get a basic api call setup with redux and axios in React Native.
This is my reducer index.js
import { combineReducers } from 'redux'
import LibaryReducer from './LibraryReducer'
import ImportLibraryReducer from './ImportLibraryReducer'
let defaultState = {
card: null
}
const mainReducer = (state = defaultState, action) => {
if(action.type === "CHANGE_CARDS") {
return {
...state,
card: action.card
}
} else {
return {
...state
}
}
}
export default mainReducer
This is my action index.js
import axios from "axios"
export function loadCards(){
return(dispatch)=>{
return axios.get('http://localhost:4000/reports')
.then(response => {
dispatch(changeCards(response.data))
})
}
}
export function changeCards(cards) {
return{
type: "CHANGE_CARDS",
card: card
}
}
This is my app.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import MainPage from './components/MainPage'
import { Header } from "native-base"
import Card from './components/Card'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducers from './reducers'
const store = createStore(reducers, applyMiddleware(thunk))
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<Provider store={store}>
<View>
<Header ><Text>hello</Text></Header>
<Card />
</View>
</Provider>
);
}
}
And, finally, this is where I'm trying to retrieve the data from the api call:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import {Collapse,CollapseHeader, CollapseBody, AccordionList} from 'accordion-collapse-react-native';
import { connect } from 'react-redux'
import * as actions from '../actions'
class Card extends Component {
render() {
const titleStyle = {
backgroundColor: '#edeeef',
fontWeight: "bold",
color: '#454647',
fontSize: 16,
left: 8,
fontFamily: 'Ionicons',
top: 10
}
const descMarkStyle = {
left: 8,
top: 4,
fontFamily: 'Ionicons',
color: '#454647',
fontSize: 16
}
console.log('in the render', this.props)
return (
<View>
<Collapse >
<CollapseHeader>
<View
style={{
backgroundColor: '#edeeef',
height: 38,
postion: 'absolute',
borderBottomWidth: .5,
borderBottomColor: '#black'
}}
>
<Text style={titleStyle}>
test
</Text>
</View>
</CollapseHeader>
<CollapseBody>
<Text style={descMarkStyle}>test</Text>
<Text style={descMarkStyle}>test</Text>
</CollapseBody>
</Collapse>
</View>
);
}
}
function mapStateToProps(state) {
return {
state
};
}
export default connect(mapStateToProps)(Card);
When I try to console log this.props in the component above, I get the default state of card: null without the api running: https://imgur.com/a/acB40KU
I'm new to redux, and I feel like there is something obvious that I'm missing.
You should trigger your action in the componentDidMount lifecycle method in your Card component. Also, you can destructure your actions in your imports and in your connect.
import { loadCards } from '../actions'
class Card extends Component {
componentDidMount() {
this.props.loadCards()
}
And in connect:
export default connect(mapStateToProps, { loadCards })(Card);
Also in the changeCards action:
card: cards
Here is how to set up axios with redux hooks and react-native in 4 steps:
source code: https://github.com/trackmystories/Redux-hooks-counter-app-with-axios.
Step 1:
create an actions.js file:
actions.js
export const TOTAL_COUNT = "TOTAL_COUNT";
export const totalCount = (data) => ({
type: TOTAL_COUNT,
data,
});
Step 2:
define and combine your reducers:
reducer.js
import { combineReducers } from "redux";
import { TOTAL_COUNT } from "./actions";
let dataState = { data: [] };
const total_counts = (state = dataState, action) => {
switch (action.type) {
case TOTAL_COUNT:
return { ...state, data: action.data };
default:
return state;
}
};
const counter = (state = 0, action) => {
switch (action.type) {
case "ADD":
return state + 1;
case "SUBTRACT":
return state - 1;
default:
return state;
}
};
const rootReducer = combineReducers({
counter,
total_counts,
});
export default rootReducer;
Step 3
create an axios get request and put request as defined in the example below and dispatch and get data.
With hooks you don't need to use connect mapStateToProps and dispatchStateToProps with redux hooks instead use { useDispatch, useSelector }.
We can pass the actions "ADD" and "SUBTRACT" inside of the button directly, without defining an action.js file.
CounterComponent.js
import React, { useEffect, useState } from "react";
import { StyleSheet, Text, View, ActivityIndicator } from "react-native";
import ActionButton from "./ActionButton";
import SubmitButton from "./SubmitButton";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { totalCount } from "../actions";
export default function CounterComponent() {
const dispatch = useDispatch();
const [isFetching, setIsFetching] = useState(false);
const total_counts = useSelector((state) => state.total_counts);
const counter = useSelector((state) => state.counter);
const { data } = total_counts;
useEffect(() => getTotalCount(), []);
const getTotalCount = () => {
setIsFetching(true);
let url = "https://url.firebaseio.com<name>.json";
axios
.get(url)
.then((res) => res.data)
.then((data) => dispatch(totalCount(data)))
.catch((error) => alert(error.message))
.finally(() => setIsFetching(false));
};
const onSubmit = (counterState) => {
let url = "https://url.firebaseio.com<name>.json";
axios.put(url, counterState).then((response) => {
console.log(response);
});
};
return (
<View>
<ActionButton
onPress={() =>
dispatch({
type: "SUBTRACT",
})
}
title="subtract"
/>
<View>
{isFetching ? (
<ActivityIndicator />
) : (
<View>
<Text>
Current state:
{data.counter ? data.counter : counter}
</Text>
</View>
)}
</View>
<ActionButton
onPress={() =>
dispatch({
type: "ADD",
})
}
title="add"
/>
<SubmitButton
onPress={onSubmit({
counterState: counter,
})}
title="Submit"
/>
</View>
);
}
Step 4:
Lastly link your RootReducer to the createStore and pass it to the Provider.
import React from "react";
import { Text, View } from "react-native";
import { Provider } from "react-redux";
import { createStore } from "redux";
import CounterComponent from "./src/components/CounterComponent";
import rootReducer from "./src/reducer";
const store = createStore(rootReducer);
export default function App() {
return (
<View>
<Text>Counter example with Redux Hooks and Axios</Text>
<Provider store={store}>
<CounterComponent />
</Provider>
</View>
);
}
I am trying to refresh a react component state based on the props.
I have this file which is the main a child component for a screen:
RoomsList.js
import React from 'react';
import { View, ActivityIndicator, StyleSheet } from 'react-native';
import {connect} from "react-redux";
import {getRooms} from "../../store/actions";
import RoomIcon from "../RoomIcon/RoomIcon";
class RoomList extends React.Component {
componentDidMount() {
this.props.onGetRooms();
}
renderRooms() {
return this.props.rooms.map(room => {
return (
<RoomIcon key={room.id} room={room} />
)
});
}
render() {
return (
<View style={styles.container}>
{ this.props.rooms.length ? this.renderRooms() : <ActivityIndicator /> }
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
flexWrap: 'wrap',
}
});
const mapStateToProps = state => {
return {
rooms: state.rooms.rooms
}
};
const mapDispatchToProps = dispatch => {
return {
onGetRooms: () => dispatch(getRooms())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(RoomList);
Rooms Reducer
import { SET_ROOMS } from '../actions/actionTypes';
const initialState = {
rooms: []
};
const roomsReducer = (state = initialState, action) => {
switch (action.type) {
case SET_ROOMS:
return {
...state,
rooms: action.rooms
};
default:
return state;
}
};
export default roomsReducer;
When the state is getting updated within the mapStateToProps function, which I can confirm it is doing as I put a console log inside of there to get the rooms and the object is the updated object.
However, it appears the the render isn't actually getting updated although the state is getting updated. I have tried to do a componentWillReceiveProps and assign the state but the state is never actually updated within here.
Rooms Action
import {SET_ROOMS} from './actionTypes';
import store from "../index";
export const getRooms = () => {
return dispatch => {
fetch("http://localhost/rooms").catch(err => {
console.log(err)
}).then(res => {
res.json();
}).then(parsedRes => {
dispatch(setRooms(parsedRes));
})
}
};
export const addRoom = (roomName, roomDescription) => {
const rooms = store.getState().rooms.rooms;
const room = {
room_name: roomName,
room_description: roomDescription
};
return dispatch => {
fetch("http://localhost/rooms", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(room)
}).catch(err => {
console.log(err)
}).then(res => res.json())
.then(parsedRes => {
rooms.push(parsedRes);
dispatch(setRooms(rooms));
})
}
};
export const setRooms = rooms => {
return {
type: SET_ROOMS,
rooms: rooms
}
};
Initialising Redux Store
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
const composeEnhancers = compose;
const store = createStore(reducers, composeEnhancers(applyMiddleware(thunk)));
export default store;
Initializing Reducers
import {combineReducers} from "redux";
import lightsReducer from "./lights";
import roomsReducer from "./rooms";
import modalsReducer from "./modals";
export default combineReducers({
lights: lightsReducer,
rooms: roomsReducer,
modals: modalsReducer
});
I am making a mobile app by React Native and Redux.
It seems that my actions, state are working properly because I did console.log them.
The issue here is that my reducer is not called.
I did log it on the console but there is no result.
I don't know what is causing this.
I try to fetch API data in Market.js
I'd really appreciated if anyone helps me.
my github repo : https://github.com/sj602/invescoin
code as following:
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { Stacks } from './utils/Navigation';
import {
StyleSheet, Text, View,
} from 'react-native';
import { store } from './store';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Stacks />
</Provider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
store.js
import {
createStore,
applyMiddleware,
compose
} from 'redux';
import promise from 'redux-promise';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reducer from '../reducers';
// const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export const store = createStore(
reducer,
// composeEnhancers(
applyMiddleware(thunk)
// )
);
reducer.js
import { combineReducers } from 'redux';
import {
GET_MARKET_CAP,
GET_MARKET_CAP_SUCCESS,
GET_MARKET_CAP_FAIL,
GET_GLOBAL_INFO,
} from '../actions';
const initialState = {
isFetching: null,
data: '',
hasError: false,
errorMessage: null,
}
export default function reducer(state = initialState, action) {
switch (action.type) {
case GET_MARKET_CAP:
console.log(2)
return ;
case GET_MARKET_CAP_SUCCESS:
console.log('success')
case GET_MARKET_CAP_FAIL:
return {
...state,
marketCap: action.err
}
case GET_GLOBAL_INFO:
console.log('action')
return {
...state,
bitcoinPercentage: action.bitcoinPercentage
}
default:
return state;
}
}
actions.js
import {
GET_MARKET_CAP,
GET_MARKET_CAP_SUCCESS,
GET_MARKET_CAP_FAIL,
GET_GLOBAL_INFO
} from './types.js';
import * as api from '../utils/api';
export const getMarketCap = (coin, currency) => dispatch => {
return api.getMarketCap(coin, currency)
.then(data => {
dispatch({type: GET_MARKET_CAP_SUCCESS, data})
})
.catch(err => {
dispatch({type: GET_MARKET_CAP_FAIL, err})
})
}
export function getGlobalInfo() {
return dispatch => {
return api.getGlobalInfo().then(data => {
dispatch({type: GET_GLOBAL_INFO, data})
})
}
}
types.js
export const GET_MARKET_CAP = 'GET_MARKET_CAP';
export const GET_MARKET_CAP_SUCCESS = 'GET_MARKET_CAP_SUCCESS';
export const GET_MARKET_CAP_FAIL = 'GET_MARKET_CAP_FAIL';
export const GET_GLOBAL_INFO = 'GET_GLOBAL_INFO';
export const GET_COIN_PRICE = 'GET_COIN_PRICE';
Market.js
import React, { Component } from 'react';
import {
View, Text, TouchableOpacity,
ScrollView, TextInput
} from 'react-native';
import { connect } from 'react-redux';
import {
getMarketCap,
getGlobalInfo,
} from '../actions/index';
import * as api from '../utils/api';
import { cryptoList } from '../utils/cryptoList';
class Market extends Component {
componentDidMount() {
this.props.getMarketCap('bitcoin', 'KRW').then(data => data[0]['market_cap_krw'])
;
this.props.getGlobalInfo();
}
render() {
return (
<View>
<Text>
{this.props.market}
</Text>
<Text>
{this.props.bitcoinPercentage}
</Text>
</View>
)
}
}
const mapStateToProps = (state) => {
return {
market: state.market,
bitcoinPercentage: state.bitcoinPercentage
}
}
export default connect(mapStateToProps, {
getMarketCap,
getGlobalInfo
})(Market)
api.js
const Coinmarketcap_URL = `https://api.coinmarketcap.com/v1/`;
const headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authentication': 'c23R30cm2jOOExyAsG6pf5Xxy4QwpndxaIMRs6aOZxIQoUlMVOv1tCQZL3jZz'
};
export const getMarketCap = (coin, currency) => {
return fetch(
`${Coinmarketcap_URL}ticker/${coin}/?convert=${currency}`,
{
method: 'GET',
headers,
}
)
.then(res => res.json())
.catch(e => console.log('Error occurred : ', e))
}
export const getGlobalInfo = () => {
return fetch(
`${Coinmarketcap_URL}global/`,
{
method: 'GET',
headers,
}
)
.then(res => res.json())
.catch(e => console.log('Error occurred : ', e))
}
I think the issue is in reducer.js L7:
import { combineReducers } from 'redux';
import {
GET_MARKET_CAP,
GET_MARKET_CAP_SUCCESS,
GET_MARKET_CAP_FAIL,
GET_GLOBAL_INFO,
} from '../actions'; // <--- ../actions/index.js doesn't export these.
When you import from ../actions, the exports are expected to be in the index.js file that is in that folder. The filenames you used to describe your issue don't match the actual filenames in your repository.
Instead, try import { ... } from ../actions/types since types.js is the file that actually exports those consts.
Hope that helps!
I think the problem is in market.js file
import { bindActionCreators } from "redux"
function mapDispatchToProps(dipatch) {
return bindActionCreators({
getMarketCap,
getGlobalInfo
}, dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(Market)
Now it should work , you missed binding your function with dispatch
Sorry, but I couldn't help but notice that is your store.js there is no initial state for the reducers. In the react docs for the create store method:
createStore(reducer, [preloadedState], [enhancer])
Can you make an initial State and see if that works. So your code might look similar to this;
import {
createStore,
applyMiddleware,
compose
} from 'redux';
import promise from 'redux-promise';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reducer from '../reducers';
initialState = {Initial State of Reducers}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export const store = createStore(
reducer,
intialState,
composeEnhancers(
applyMiddleware(thunk)
)
);