I am trying to update the initialData for my app with an API call.
I am getting the error: (0,_configureStore2.default) is not a function.
Here is my react/ redux setup,
// app.js
import { Provider } from 'react-redux'
import configureStore from './config/configureStore';
import AppNavigation from './navigation'
import { eventsFetchData } from './actions/events'
const store = configureStore();
// dispatch action to get array of events for app
store.dispatch(eventsFetchData());
export default class App extends Component {
render() {
return (
<Provider store={store}>
<AppNavigation />
</Provider>
);
}
}
I am using a thunk to get my events data:
export const eventsHasErrored = bool => ({
type: 'EVENTS_HAS_ERRORED',
hasErrored: bool
})
export const eventsIsLoading = bool => ({
type: 'EVENTS_IS_LOADING',
isLoading: bool
})
export const eventsFetchDataSuccess = events => ({
type: 'EVENTS_FETCH_DATA_SUCCESS',
events
})
export function eventsFetchData(url) {
return (dispatch) => {
dispatch(eventsIsLoading(true));
fetch(url)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(eventsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((events) => dispatch(eventsFetchDataSuccess(items)))
.catch(() => dispatch(eventsHasErrored(true)));
};
}
My reducers are:
export function eventsHasErrored(state = false, action) {
switch (action.type) {
case 'EVENTS_HAS_ERRORED':
return action.hasErrored;
default:
return state;
}
}
export function eventsIsLoading(state = false, action) {
switch (action.type) {
case 'EVENTS_IS_LOADING':
return action.isLoading;
default:
return state;
}
}
export function events(state = [], action) {
switch (action.type) {
case 'EVENTS_FETCH_DATA_SUCCESS':
return {
...state,
...action.events,
};
default:
return state;
}
}
export function calendarView(state = null, action) {
switch (action.type) {
case 'CALENDAR_VIEW':
return action.viewType;
default:
return state;
}
}
This is my store:
const initialState = {
eventsHasErrored: false,
eventsIsLoading: true,
events: [],
calendarView: 0
};
const reduxLogger = createLogger();
const store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk, reduxPromise, reduxLogger)
);
export default store;
How would be best to update my initialState from an api call?
Try:
// app.js
import { Provider } from 'react-redux'
import store from './config/configureStore';
import AppNavigation from './navigation'
import { eventsFetchData } from './actions/events'
// dispatch action to get array of events for app
store.dispatch(eventsFetchData());
export default class App extends Component {
render() {
return (
<Provider store={store}>
<AppNavigation />
</Provider>
);
}
}
That's because I see that you return already created store and this would explain the error message:
const store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk, reduxPromise, reduxLogger)
);
export default store;
Related
I'm creating a library using create-react-library. My library uses typescript, hooks and redux.
I think that my problem is by typescript or hooks.. because I've tried different ways to export and always show same error.
./src/redux/RootReducer.tsx
Attempted import error: 'react-tree-library' does not contain a default export (imported as 'HierarchyTreeReducerState').
I've tried:
Use export const
// Export a variable
export const App = () => { ... }
// Import App in another file
import { App } from '...'
Use export default
// Export default
const App = () => { ... }
export default App
// Import App in another file
import App from "...";
// And this option
import { App } from "...";
As you can see:
const MainTree = ({
data,
portal,
branch,
...
}: MainProps) => { ... }
const mapStateToProps = createStructuredSelector({
branch: makeSelectBranchItem(),
hierarchy: makeSelectHierarchyItem(),
});
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
...
dispatch,
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(withConnect)(MainTree);
Reducers:
const HierarchyTreeReducerState = (state = initialState, action: any) => {
switch (action.type) {
case HierarchyTreeConstants.SET_SELECTED: {
return Object.assign({}, state, {
selected: action.payload,
});
}
case HierarchyTreeConstants.SET_DMA: {
return Object.assign({}, state, {
selectedDma: action.payload,
});
}
case HierarchyTreeConstants.SET_HIDDEN_BRANCHS: {
return Object.assign({}, state, {
hiddenBranchs: action.payload,
});
}
default:
return state;
}
};
export default HierarchyTreeReducerState;
const TreeReducerState = (state = initialState, action: any) => {
switch (action.type) {
case TreeConstants.SET_NUMBRANCHS: {
return Object.assign({}, state, {
numBranchs: action.payload
});
}
case TreeConstants.SET_BRANCH: {
return Object.assign({}, state, {
branchs: action.payload,
});
}
default:
return state;
}
};
export default TreeReducerState;
index.ts of Library:
export const TreeGoaiguaLibrary = ({
portal,
removeBranchWithChildren,
data,
}: Props) => {
return (
<Main
removeBranchWithChildren={removeBranchWithChildren}
data={data}
portal={portal}
/>
);
};
export { TreeGoaiguaLibrary , TreeReducer, HierarchyTreeReducer };
And when I do yarn link to library, I import in RootReducer of other project to use my library I do this:
import { combineReducers } from "redux";
import TreeReducerState from "react-goaigua-tree-library";
import HierarchyTreeReducerState from "react-goaigua-tree-library";
const combinedReducers = combineReducers({
branch: TreeReducerState,
hierarchy: HierarchyTreeReducerState,
} as any);
export const RootReducer = (state: any, action: never): any => {
return combinedReducers(state, action);
};
And show the error:
./src/redux/RootReducer.tsx
Attempted import error: 'react-tree-library' does not contain a default export (imported as 'HierarchyTreeReducerState').
I've solved ( I think )
index.ts of Library
import MainTree from "./components/main";
import HierarchyTreeReducerState from "./redux/reducers/HierarchyTreeReducer";
import TreeReducerState from "./redux/reducers/TreeReducer";
export { MainTree };
export default { TreeReducerState, HierarchyTreeReducerState };
Reducers:
export const HierarchyTreeReducerState = (state = initialState, action: any) => {
switch (action.type) {
case HierarchyTreeConstants.SET_SELECTED: {
return Object.assign({}, state, {
selected: action.payload,
});
}
case HierarchyTreeConstants.SET_DMA: {
return Object.assign({}, state, {
selectedDma: action.payload,
});
}
case HierarchyTreeConstants.SET_HIDDEN_BRANCHS: {
return Object.assign({}, state, {
hiddenBranchs: action.payload,
});
}
default:
return state;
}
};
export default HierarchyTreeReducerState;
export const TreeReducerState = (state = initialState, action: any) => {
switch (action.type) {
case TreeConstants.SET_NUMBRANCHS: {
return Object.assign({}, state, {
numBranchs: action.payload
});
}
case TreeConstants.SET_BRANCH: {
return Object.assign({}, state, {
branchs: action.payload,
});
}
default:
return state;
}
};
export default TreeReducerState;
Now show me this error:
I created an action creator that is simply supposed to make a get request to my API and return with a list of all projects. However, for some reason, my return dispatch in my thunk function is not firing at all. It gets to the console.log() statement and just ends. There are no consoles errors, and no network calls being made either as far as I can tell. Any ideas why it would do absolutely nothing?
Dashboard.js (component)
import ProjectItem from "../Project/ProjectItem";
import styles from "./Dashboard.module.css";
import CreateProjectButton from "../CreateProjectButton/CreateProjectButton";
import { connect } from "react-redux";
import { getProjects } from "../../Redux/getProjects/actions";
const Dashboard = props => {
useEffect(() => {
console.log("blah");
getProjects();
}, []);
return (
<div className={styles.dashboardContainer}>
<h1>Projects</h1>
<br />
<CreateProjectButton />
<br />
<hr />
<ProjectItem />
</div>
);
};
const mapStateToProps = state => {
return {
projects: state
};
};
const mapDispatchToProps = dispatch => {
return {
getProjects: () => dispatch(getProjects())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
action.js (action creator)
import { GET_PROJECTS_SUCCESS, GET_PROJECTS_ERROR } from "./constants";
export const getProjectsSuccess = payload => {
console.log("getProjectSuccess", payload);
return {
type: GET_PROJECTS_SUCCESS,
payload
};
};
export const getProjectsError = () => {
console.log("there was an error");
return {
type: GET_PROJECTS_ERROR
};
};
export function getProjects() {
console.log("getProject");
return dispatch => {
axios
.get("/project/all")
.then(res => dispatch(getProjectsSuccess(res.data)))
.catch(err => dispatch(getProjectsError(err)));
};
}
index.js (getProject reducer)
const initialState = {
projects: [], //array of projects
project: {}, // single project for update case
reRender: false
};
const getProjectsReducer = (state = initialState, action) => {
switch (action.type) {
case GET_PROJECTS_SUCCESS:
return { ...state, projects: action.payload }; // will need to change action.payload later on
default:
return state;
}
};
export default getProjectsReducer;
constants.js
export const GET_PROJECTS_SUCCESS = "GET_PROJECTS_SUCCESS";
export const GET_PROJECTS_ERROR = "GET_PROJECTS_ERROR";
rootReducer.js
import createProjectReducer from "./createProject/index";
import getProjectsReducer from "./getProjects/index";
const rootReducer = (state = {}, action) => {
return {
project: createProjectReducer(state.project, action),
projects: getProjectsReducer(state.projects, action)
};
};
export default rootReducer;
FIXED: After reading up on the use effect hook in functional components I realized I was missing props.getProjects in the useEffect function in dashboard.js!
I am pretty new to Redux and the whole Redux-Saga thing and wanted to use React-Boilerplate to try a small project that basically just makes an API call and iterates over the data. And I currently have a problem I've been stuck at for hours. Maybe you have an idea?
My React Component looks like this:
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import { useInjectSaga } from 'utils/injectSaga';
import { useInjectReducer } from 'utils/injectReducer';
import {
makeSelectDevices,
makeSelectLoading,
makeSelectError
} from './selectors';
import reducer from './reducer';
import { fetchDevices } from './actions';
import saga from './saga';
export function LeafletMap(props) {
const {devices, loading, error, fetchDevices } = props;
useInjectReducer({ key: 'leafletMap', reducer });
useInjectSaga({ key: 'leafletMap', saga });
useEffect(() => {
fetchDevices();
}, [fetchDevices]);
if (loading) return(<div>Loading...</div>)
return (
<div>
{ !error ?
<Map center={[47.3, 9.9]} zoom={9} style={{height: '500px'}}>
<TileLayer
url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
attribution='© OpenStreetMap contributors'
/>
{ devices && devices.map((device)=> {
let coordinates = [device.latitude, device.longitude];
return (
<Marker key={device.id} position={coordinates}></Marker>
);
})}
</Map>
: ''
}
</div>
);
};
LeafletMap.propTypes = {
devices: PropTypes.array,
loading: PropTypes.bool,
error: PropTypes.any,
};
const mapStateToProps = createStructuredSelector({
devices: makeSelectDevices(),
loading: makeSelectLoading(),
error: makeSelectError(),
});
function mapDispatchToProps(dispatch) {
return {
fetchDevices: () => dispatch(fetchDevices())
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
export default compose(withConnect)(LeafletMap);
When my component mounts I use the useEffect Hook to dispatch an action that I bound to my props using mapDispatchToProps. The actions file looks like this:
import {
FETCH_DATA,
FETCH_DATA_ERROR,
FETCH_DATA_SUCCESS,
CLICK_DEVICE
} from './constants';
export function fetchDevices() {
return {
type: FETCH_DATA,
};
}
export function fetchDevicesSuccess(devices) {
return {
type: FETCH_DATA_SUCCESS,
devices
};
}
export function fetchDevicesError(error) {
return {
type: FETCH_DATA_ERROR,
error
};
}
My saga then reacts to the FETCH_DATA action and calls a generator to fetch the data from my local API:
import { all, call, put, takeEvery } from 'redux-saga/effects';
import request from 'utils/request';
import { fetchDevicesSuccess, fetchDevicesError } from './actions';
import { FETCH_DATA } from './constants';
function* fetchDevicesAsync() {
yield takeEvery(FETCH_DATA, fetchAllDevices);
}
function* fetchAllDevices() {
try {
const requestUrl = '/api/devices';
const devices = yield call(request, requestUrl);
yield put(fetchDevicesSuccess(devices));
} catch (error) {
yield put(fetchDevicesError(error.toString()));
}
}
export default function* rootSaga() {
yield all([fetchDevicesAsync()]);
}
This in return should trigger my reducer which looks as follows:
import produce from 'immer';
import {
FETCH_DATA,
FETCH_DATA_ERROR,
FETCH_DATA_SUCCESS,
} from './constants';
export const initialState = {
devices: [],
loading: true,
error: false,
};
/* eslint-disable default-case, no-param-reassign */
const leafletMapReducer = (state = initialState, action) =>
produce(state, () => {
switch (action.type) {
case FETCH_DATA:
state.loading = true;
state.error = false;
break;
case FETCH_DATA_ERROR:
state.loading = false
state.error = action.error;
break;
case FETCH_DATA_SUCCESS:
state.loading = false;
state.error = false;
state.devices = action.devices;
break;
}
});
export default leafletMapReducer;
My problem here is that everything seems to work but my action is neither being displayed in Redux DevTools nor does my component update after the initial render. It seems as if the action is being dispatched before the ##INIT event.
Any idea why this happens?
Thanks in advance!
EDIT:
Just in case it has something to do with my selectors:
import { createSelector } from 'reselect';
import { initialState } from './reducer';
/**
* Direct selector to the leafletMap state domain
*/
const selectLeafletMapDomain = state => state.leafletMap || initialState;
/**
* Other specific selectors
*/
const makeSelectDevices = () =>
createSelector(
selectLeafletMapDomain,
leafletMapState => leafletMapState.devices
);
const makeSelectLoading = () =>
createSelector(
selectLeafletMapDomain,
leafletMapState => leafletMapState.loading,
);
const makeSelectError = () =>
createSelector(
selectLeafletMapDomain,
leafletMapState => leafletMapState.error,
);
/**
* Default selector used by LeafletMap
*/
const makeSelectLeafletMap = () =>
createSelector(selectLeafletMapDomain, leafletMapState => leafletMapState.toJS());
export default makeSelectLeafletMap;
export {
selectLeafletMapDomain,
makeSelectDevices,
makeSelectLoading,
makeSelectError
};
Found the problem myself :)
The problem was in my reducer:
const leafletMapReducer = (state = initialState, action) =>
produce(state, () => { // <-- here
switch (action.type) {
case FETCH_DATA:
state.loading = true;
state.error = false;
break;
I here wrongly mutated my state which leads to the error. The correct solution is:
const leafletMapReducer = (state = initialState, action) =>
produce(state, draftState => { // use draftState instead of normal state
switch (action.type) {
case FETCH_DATA:
draftState.loading = true; //<------
draftState.error = false; //<------
break;
My problem is that mapStateToProps returns undefined. Maybe I have some problems with dispatching in the state or maybe app rendering before data comes from the server? I can't understand. So app works right without redux with just componentDidMount, but I have some problems with redux
So I have a top-level component App:
const App = () => {
return (
<Provider store={store}>
<Screen />
</Provider>
)
}
I have store with thunk meddleware:
const store = createStore(reducer, applyMiddleware(ReduxThunk));
Two types of action:
export const fetchData = (newPhotos) => async (dispatch) => {
function onSuccess(success) {
dispatch({
type: FETCH_DATA,
payload: success})
return success
}
function onError(error) {
dispatch({type: FETCH_FAILED, error})
}
try {
const URL = 'https://api.unsplash.com/photos/?client_id=cf49c08b444ff4cb9e4d126b7e9f7513ba1ee58de7906e4360afc1a33d1bf4c0';
const res = await fetch(URL);
const success = await res.json();
console.log(success);
return onSuccess(success);
} catch (error) {
return onError(error)
}
};
reducer:
const initialState = {
data: []
}
export default dataReducer = (state = initialState, action) => {
console.log(action);
switch (action.type) {
case FETCH_DATA:
return {
data: action.payload
}
case FETCH_FAILED:
return {
state
}
default: return state;
}
}
combine reducers:
export default combineReducers({
fetchedData: dataReducer
});
and my rendering component:
class HomeScreen extends Component {
render() {
console.log(this.props)
const {navigation, data} = this.props;
return (
<ScrollView>
<Header />
<ImageList navigation={navigation} data={data}/>
</ScrollView>
)
}
}
const mapStateToProps = state => {
return {
data: state.fetchedData.data
}
}
export default connect(mapStateToProps, {fetchData})(HomeScreen);
fetchData action will not be called on its own. You need to call that explicitly(in componentDidMount and, probably, componentDidUpdate) like
class HomeScreen extends Component {
componentDidMount() {
this.props.fetchData(/* I don't know where you are going to take newPhotos argument */);
}
render() {
//...
}
}
I'm learning Redux state management and got stuck with an issue. The mapStateToProps within a component is not triggered when the state changes. Gone through a lot of blogs, couldn't able to figure out the problem.
The store seems to update properly, as the "subscribe" method on store prints new changes. Attached screenshot as well.
Actions.js
export const GET_ITEMS_SUCCESS = "GET_ITEMS_SUCCESS";
export const GET_ITEMS_FAILURE = "GET_ITEMS_FAILURE";
export const getItemsSuccess = (items) => ({
type: GET_ITEMS_SUCCESS, payload: items
});
export const getItemsFailure = (error) => ({
type: GET_ITEMS_FAILURE, error: error
});
export function getItems(dispatch) {
return dispatch => {
fetch(myList)
.then(res => res.json())
.then(res => {
if(res.error) {
throw(res.error);
}
store.dispatch(getItemsSuccess(res));
return res;
})
.catch(error => {
store.dispatch(getItemsFailure(error));
})
}
}
Reducer
let initialState = {items: [], error: null}
function GetItemsReducer (state = initialState, action) {
switch (action.type) {
case GET_ITEMS_SUCCESS:
return Object.assign({}, state, {pending: false, items: action.payload});
case GET_ITEMS_FAILURE:
return Object.assign({}, state, {pending: false, error: action.error});
default:
return state;
}
}
export default const rootReducer = combineReducers({
GetItemsReducer: GetItemsReducer
});
Store
const mystore = createStore(rootReducer, applyMiddleware(thunk, promise));
mystore.subscribe(() => console.log("State Changed;", mystore.getState()));
Component
class Home extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.fetchItems();
}
render() {
return (
<div>{this.props.items.length}</div>
)
}
}
const mapStateToProps = (state) => {
console.log('mapStateToProps ----------> ', state);
return {
items: state.GetItemsReducer.items,
error: state.GetItemsReducer.error
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchItems: bindActionCreators(getItems, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Main
class App extends React.Component {
render() {
return (
<Provider store={mystore}>
<Home />
</Provider>
)
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
Thanks in advance.