hello guys I am trying to fetch initial data for redux store from given api !
here is my first approach from using store.dispatch:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
import { fetchNews } from "./actions";
const store = createStore(
reducer,
compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
store.dispatch(fetchNews());
export default store;
and second approach is dispatching action from the component lifecycle method like this:
import React from "react";
import axios from "axios";
import InfiniteScroll from "react-infinite-scroller";
import { useSelector, useDispatch } from "react-redux";
import New from "./New";
import { fetchNews } from "../store/actions";
const Main = () => {
const news = useSelector(state => state.news);
const dispatch = useDispatch();
React.useEffect(() => {
dispatch(fetchNews());
}, [dispatch]);
return (
<>
{news.length > 0 ? (
news.map(data => (
<New key={data.id} subTitle={data.created_at}>
{data.title}
</New>
))
) : (
<p>News not found</p>
)}
</>
);
};
OK both method works but i really want to know what is the better approach here or is there any other better method! thank you!
Dispatching action from the component lifecycle method is the recommended approach and is what should be followed in order to maintain the separation of concern design on which Redux is based on please check the attached image.
The second one. The file in which you create the store should always be all about creating the store and exporting it as is.
By the way, on your useEffect, try removing the dispatch from the variables array:
React.useEffect(() => {
dispatch(fetchNews());
}, []);
This way it will fetch the data on component mount, just like ComponentDidMount.
I wouldn't say dispatching from component lifecycle is the recommended approach , it certainly is used by many developers, but it couples in you component with data fetching (is that a good idea) ? Or should your component not know about how data is fetched and just consume it in some way. Your choice.
You could use your own custom middleware to send the request.
With that approach you have greater flexibility , you could dispatch actions like type:LOADING_DATA and any others depending on setting state while loading your data. Once the data is returned dispatch other action(s) which could be handled by other custom middleware(s) to normalise data etc and finally send it to your reducer.
This approach is described very well in https://leanpub.com/thinking-in-Redux or https://www.youtube.com/watch?v=JUuic7mEs-s
I personally liked his ideas and perhaps you would find them useful.
Related
I have requirement where I have to use RTK query for data fetching and Redux-thunk for global state management. For the same I have created 2 separate stores, one for RTK and another for redux. Now I am facing issue as we should not have 2 stores in our application.
store 1:-
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import combineReducer from './combineReducer';
export const configureStore = () : any => {
const middleWares = [thunk];
const middlewareEnhancer = applyMiddleware(...middleWares);
const enhancers = [middlewareEnhancer];
const composedEnhancers: any = compose(...enhancers);
const store = createStore(combineReducer, composedEnhancers);
return store;
};
store 2:-
import { configureStore } from "#reduxjs/toolkit";
import { setupListeners } from "#reduxjs/toolkit/query";
import { postApi } from "../services/posts";
export const store = configureStore({
reducer: {
[postApi.reducerPath]: postApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(postApi.middleware),
});
setupListeners(store.dispatch);
App.tsx:-
const App: React.FC = () => (
<Provider store={store}>
<Provider store={rtkStore}>
<Suspense fallback={<div>Loading...</div>}>
<BrowserRouter>
<Routes />
</BrowserRouter>
</Suspense>
</Provider>
</Provider>
);
I am sure, there is something fishy in this. Any solution for requirement would be appreciable.
Redux Toolkit comes with thunk per default. You do not need to set it up. RTK Query internally is based on thunks. You cannot have multiple Redux stores in one application.
Just create one store with configureStore, also add your other reducers there and use that for everything.
Generally we recommend in the Redux style guide to use Redux Toolkit for all your Redux logic, as helpers like createSlice massively reduce your boilerplate code and guard against most common bugs.
This is the default recommendation for over two years now.
I would highly recommend you to read the official Redux Essentials tutorial if you haven't already.
The following React component is given:
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { store, StoreState } from "../../redux/actions";
import { setBackgroundAction } from "../../redux/title.actions";
import "./Loader.scss";
interface ReduxProps {
bgClass: string;
}
interface Props extends ReduxProps {
bgChange?: boolean;
}
export default function Loader(props: Props) {
const [bgClassOld, setBgClassOld] = useState<string>("");
const dispatch = useDispatch();
useEffect(() => {
const { bgChange, bgClass } = props;
if (bgChange) {
setBgClassOld(bgClass);
dispatch(setBackgroundAction("bg-white"));
dispatch(setBackgroundAction(bgClassOld));
}
});
return (
<div className="d-flex">
<div className="loader">
<img src="/loadscreen.gif" />
</div>
</div>
);
}
// function mapping(state: StoreState): ReduxProps {
// return {
// bgClass: state.title.backgroundClass,
// };
// }
This is more a theoretical question to see how to actually do the following change:
The component Loader will be imported from another npm package (shared components).
My problem is that I have a redux state in the current implementation included (changed it from Class to Functional component, so thats mapping() is still in there).
As I only import the component in my "main" client, I will not have the whole redux setup in place. So I think I need to pass the store and the dispatch functions via props.
So should I create a prop store for my component, where I pass the redux store when I import the shared component?
Do I also create two props for each dispatch functions?
Does is make sense or would there be a better approach?
You generally shouldn't import the Redux store directly into components. The hooks allow your component to access whatever Redux store has been injected into the component tree by a <Provider>.
You also don't need to pass dispatch as a prop. Any component can call useDispatch(), and dispatch actions to whatever Redux store is actually being used.
If I understand your question, you're planning on importing this component into an existing app, and it sounds like that app is already configured to use (React-)Redux with a <Provider> at the top. If that's the case, then you don't have to do anything else special to make this work. Just call the React-Redux hooks in any of your components.
My question is related to react-admin repo.
I want to dispatch an action, outside of scope of a component, in order to do that, I've read that I need to get access to the actual redux store itself, and dispatch on in directly,
so I know that the Admin component has an initialState prop, but it only accepts default state object, not the store. So I can't make a store and pass it in.
My question is:
How do I access redux store of an Admin component?
How can I dispatch an action outside of a component, when using Admin as my main app component?
my current app entry looks like this:
<AppLayoutDirection>
<Admin
title="My App"
locale="en"
dataProvider={dataProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
theme={themeProvider}
customSagas={customSagas}
appLayout={AppLayout}
>
{DynamicResource}
</Admin>
</AppLayoutDirection>
When you say that you need to dispatch an action outside the scope of a component, I suppose that it's in reaction to another action that was dispatched in the past.
In that case, that's what react-admin calls a side effect. React-admin handles side effects using redux-saga. Here is how to create a custom saga:
// in src/bitcoinSaga.js
import { put, takeEvery } from 'redux-saga/effects';
import { showNotification } from 'react-admin';
export default function* bitcoinSaga() {
yield takeEvery('BITCOIN_RATE_RECEIVED', function* () {
yield put(showNotification('Bitcoin rate updated'));
})
}
Register this saga in the <Admin> component as follows:
// in src/App.js
import React from 'react';
import { Admin } from 'react-admin';
import bitcoinSaga from './bitcoinSaga';
const App = () => (
<Admin customSagas={[ bitcoinSaga ]} dataProvider={simpleRestProvider('http://path.to.my.api')}>
...
</Admin>
);
export default App;
This is documented in the react-admin documentation, in the <Admin> chapter.
You could also simply use custom reducers if the computation is no async
// in src/App.js
import React from 'react';
import { Admin } from 'react-admin';
import reducers from './reducers';
const App = () => (
<Admin customReducers={customReducers} dataProvider={simpleRestProvider('http://path.to.my.api')}>
...
</Admin>
);
export default App;
Is something like this bad practice?
SendInfoButton.js
import React from 'react';
import { sendInfo } from '../actions/index';
export const SendInfoButton = ({currentUser}) => (
<div>
<button onClick={() => sendInfo(currentUser)} />
</div>
)
actions/index.js
import { store } from '../reducers/index';
import { SEND_INFO } from '../constants/index;
export const sendInfo = (currentUser) => store.dispatch({type: SEND_INFO, payload: currentUser})
It seems more efficient to import actions directly into the components this way, as opposed to using mapDispatchToProps and passing down actions to components that won't use them. I'm also more inclined to import actions like this because I already have components with a large number of props and would rather not add to that.
Importing the action creator, like import { sendInfo } from '../actions/index';, is fine - that's how you're supposed to do it.
However, you should then use connect to "bind" the action creators so that they access the correct store instance at runtime and dispatch the action automatically. This can be made shorter by using the "object shorthand" syntax - just pass an object full of action creators as the second argument to connect, like:
export default connect(null, {sendInfo})(SendInfoButton);
Similarly, you shouldn't import the store directly. As #estes said, that locks your code into the same "production" store instance all the time, and makes it harder to test or reuse your code.
I'm studying react and I have an example like this
//index.js
const store = createStore(reducer)
render(
<Provider store={store}>
<AddTodo />
</Provider>,
document.getElementById('root')
)
//Apptodo.js
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
.......
Why didn't it get this.pros.store but simply call the dispatch() function ?
EDIT: How does it extract the dispatch from this.pros. Isn't the object this.pros.store ? and in this case why don't we just extract store ?
Thank you.
react-redux is the library that is passing these methods to your component as props.
dispatch() is the method used to dispatch actions and trigger state changes to the store. react-redux is simply trying to give you convenient access to it.
Note, however, that dispatch is not available on props if you do pass in actions to your connect function. In other words, in the code below, since I'm passing someAction to connect, dispatch() is no longer available on props.
The benefit to this approach, however, is that you now have the "connected" action available on your props that will automatically be dispatched for you when you invoke it.
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { someAction } from '../myActions';
const MyComponent = (props) => {
// someAction is automatically dispatched for you
// there is no need to call dispatch(props.someAction());
props.someAction();
};
export default connect(null, { someAction })(MyComponent);
Or if we were to use object destructuring as shown in the example you give...
const MyComponent = ({ someAction }) => {
someAction();
};
It's important to note, however, that you must invoke the connected action available on props. If you tried to invoke someAction(), you'd be invoking the raw, imported action — not the connected action available on props. The example given below will NOT update the store.
const MyComponent = (props) => {
// we never destructured someAction off of props
// and we're not invoking props.someAction
// that means we're invoking the raw action that was originally imported
// this raw action is not connected, and won't be automatically dispatched
someAction();
};
This is a common bug that people run into all the time while using react-redux. Following eslint's no-shadow rule can help you avoid this pitfall.
Your addTodo component has access to the store's state and methods(e.g, dispatch, getState, etc). So, when you hooked up your React view with the Redux store via the connect method, you had access to store's state and methods.
({ dispatch }) is simply using JS destructuring assignment to extract dispatch from this.props object.