I am new in redux library of react
This is my code
// action creator
export function updateCategories(payload) {
return { type: UPDATE_CATEGORY, payload };
}
// dispatch action creator
dispatch(updateCategories({ list: data }));
But instead if I do like :
dispatch({ type: UPDATE_CATEGORY, payload });
so what is problem if I write action directly in dispatch without returning from function?
Technically it's fine, you can dispatch straight from the component, but there's advantages of having actions creators, e.g.
It creates a intuitively named, reusable function for the dispatch, which you can import as needed, rather than manually dispatching all over the place. So if you want to change how the dispatch of a certain action works, you only have to change in the action creator and not go through all the places it's used and change each one
Simply dispatching something with a payload is fine, but you may also end up having logic to go along with that dispatch, like processing of the payload or fetching of data (using some tool like redux-thunk). It's better to have a single action creator where that's all defined that you can reuse throughout your application, rather than copy pasting that logic all over the place
It helps in code re-usability.
You can go through this link for more Info
Related
I'm in the learning phase of understanding redux state management and still trying to negotiate the bewildering jungle of boilerplate code and middleware, much of which I'm taking on faith as 'good medicine'. So I hope you'll bear with me on this perhaps rudimentary question.
I know that redux-thunk allows action creators to proceed asynchronously and dispatch a regular action at a subsequent time. For example, I can define a thunk action creator in my actions.js:
export function startTracking() {
return (dispatch => {
someAsyncFunction().then(result => dispatch({
type: types.SET_TRACKING,
location: result
}))
})
}
And invoke it from within a React component like so:
onPress={() => this.props.dispatch(actions.startTracking())}
My question is, what advantage does the above code confer over simply dispatching an action from inside an asynchronous callback?
import { store } from '../setupRedux'
...
export function startTracking() {
someAsyncFunction().then(result => {
store.dispatch({
type: types.SET_TRACKING,
location: result
})
})
}
which I would invoke inside my component
onPress={() => actions.startTracking()}
or even
onPress={actions.startTracking}
Is there anything problematic with accessing store directly via an import as I'm doing in the 2nd example?
There is nothing wrong doing so. From the redux-thunk page:
If you’re not sure whether you need it, you probably don’t.
The creator of redux explain the advantage of using it here:
This looks simpler but we don’t recommend this approach. The main reason we dislike it is because it forces store to be a singleton. This makes it very hard to implement server rendering. On the server, you will want each request to have its own store, so that different users get different preloaded data.
Basically, using redux-thunk will save you the store import in each action creator file and you will be able to have multiple store. This approach also give you the opportunity to write a little bit less code and to avoid spaghetti code. Many redux developer don't like to import the store and to manually dispatch because it can create circular dependencies if the code is badly separated (importing an action name from the action creator file in the reducers file and importing the store from the reducers file in the action creator file). Also, dispatching directly an action like that might break the middleware workflow, ie: the action might not be handled by a middleware.
But honestly, if you don't see an advantage to it yet, don't use it. If one day you have trouble with async actions, redux-thunk might be the answer.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Why use Redux Thunk then one can do something like this:
ReadableAPI.getCategories().then((categories)=>{
console.log('after getCategories', categories)
this.props.dispatch(addCategories(categories))
})
Isn't this more straightforward and achieves the same thing?
This answer from Dan Abramov explains beautifully why you would want to use redux-thunk in your application. One more benefit of using redux-thunk in your application is that you keep your business logic separate from your view part (React in your case). We had a use case where our app was written in backbone and we wanted to re-write our whole app in React. We realised that it is easy if your view and collection/models are separate. We started depreciating just the html templates and not the collections. When we deprecated all the html templates, we started using Redux in our app and deprecated our collections too.
What I want to put here is that we had our view and business logic separate, we could refactor that easily. Similarly, with React and Redux, you would want to keep that different, so that if something new comes and replaces Redux, at least you won't have to deprecate your views and you just will have to change your business logic.
Redux Thunk basically allows us to delay the dispatch of an action, i.e we can handle the action returned by the action creator and call the dispatch function when we really want to dispatch.
Your example is incomplete and it was frustrating to follow how you arrived at that oversimplified solution. After researching it I realized, you probably have some ReadableAPI.js file somewhere you should have posted with what is probably a configuration using fetch and inside of it you probably have something like this:
export const getCategories = () =>
fetch('http://localhost:3001/categories', {headers})
.then(res => res.json())
.then(data => console.log(data))
which ties into your:
ReadableAPI.getCategories().then((categories)=>{
console.log('after getCategories', categories)
this.props.dispatch(addCategories(categories))
})
So in this solution you are returning a Promise which is an object which essentially gives us notification when some amount of work such as a network request is completed and in order to get notified we chain on the .then() function which we pass an arrow function like you did: then((categories)=> and that arrow function will be called at some point in the future.
It looks like you are referring to that data as categories and you are console logging 'after Categories', categories.
What we need to know is what are the different properties attached to that categories object? Does it have a data property? Does it have a results property with some actual data in it? Is there a categories.data.results that contains whatever the data is?
So let's just say the answer is yes to all the questions.
You are going about that in a bit of a hard way in order to deal with asynchronous requests because it's not just that snippet of code: there is also what's inside the ReadableAPI.js file, right? Also, you are using Promises that can get kind of hairy and you would have already put together two files just to deal with asynchronous request which would be okay if it was just a plain Reactjs application, but you mentioned your approach as an alternative to Redux-Thunk which implies using Redux.
For your approach in the vanilla Reactjs space I would use Axios and implement the async/await syntax, but with Redux involved you don't want to use a Promise.
Now, the action creator I had to make up in the ReadableAPI.js file would not work in a Redux environment because it does not return a plain JavaScript action object and so we would have to use a custom middleware as the error says to do.
So how does a middleware like Redux-Thunk work? Redux-Thunk essentially relaxes the rules around an action creator.
The purpose of Redux-Thunk is not to be passed a request object and it will take it away and go to work for you.
Redux-Thunk is an all purpose middleware that allows us to deal with asynchronous action creators, but it also allows us to do many other things as well.
With Redux Thunk involved, your action creator can return an action object. If you return an action object it still must have a type property and if it is an action object that gets returned it can optionally have a payload as well.
The other thing that Redux-Thunk does is allow you to return either an action object or a function.
If you return a function, Redux-Thunk will automatically call that function for you.
That's it, thats all Redux-Thunk does. However, One thing Redux-Thunk does really well is to manually dispatch an action. That is the key part. With Redux-Thunk we can manually dispatch an action at some point in time in the future.
So we get this new action created and it can be a plain JavaScript object or a function, but when we are dispatching it manually inside of Redux-Thunk or inside of a function it's basically always going to be a plain object.
So we will dispatch this action and it will flow back into dispatch and dispatch will send it right back into Redux-Thunk and Redux-Thunk will ask if it's an action or object.
When it's an object, Redux-Thunk forwards it automatically to all the different reducers.
With Redux-Thunk we can return a function and if we do, that function gets invoked with dispatch and getState arguments and with those two functions we have unlimited power over our Redux store and we can change any data and read any data and at any point in time in the future we can manually dispatch an action and update the data inside of our store.
Where am I getting the dispatch and getState? From the Redux-Thunk library source code:
https://github.com/reduxjs/redux-thunk/blob/master/src/index.js
src/index.js:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
If you look at the if conditional you see the body of the actual logic that is going on. Did you just dispatch an action? If so, is it a function? If it is, then Redux-Thunk is going to invoke that action with dispatch and getState.
If our action is not a function, Redux-Thunk does not care about it, so on it goes to the next middleware as indicated by the return next(action);, otherwise on to the reducers if there is no middleware to run.
I'm working on a component in React (with Redux) that has the requirements to:
Retreive data from an API endpoint.
Display that data, with one field being editable.
If the editable field is edited, it has to update the rest of the fields and send them to a different endpoint.
I'm having trouble grasping how exactly I should implement the editing using the Redux metodology.
Here is a simplified component:
class MyComponent extends Component {
constructor(...args) {
super(...args);
this.props.dispatch(getDataFromEndpoint());
}
editField(e) {
this.props.dispatch(editFieldAction(e.target.value));
}
render() {
return (
<div>
<input type="text" defaultValue={this.props.data.editableField} onChange={this.editField} />
{
this.props.data.uneditable.map(x => {
return (
<span>{x}</span>
);
})
}
</div>
);
}
}
const mapProps = state => {
const { data } = state.endpointData;
return { data };
}
export default connect(mapProps)(MyComponent);
Note that I've written this component as an example, it might have errors, but you should get the point. Whenever this.editField is called, it should update every value in this.props.data.uneditable.
What is the correct way to do this?
In a sense these questions all boil down to: How do I wire up my component(s) to read from (and write to) Redux state. This is essentially the purpose of redux-react's connect function (which you have in the bottom of your example). You use the first parameter, mapStateToProps, to be able to read from state. You use the second parameter (that you don't have yet) - mapDispatchToProps - to provide the means to write to it. Dispatching an action is how you update Redux state.
You can simply use this.props.dispatch, but it is more idiomatic to use mapDispatchToProps, which simply provides an object where each property is a function which calls dispatch with the appropriate action creator. You might accept as a parameter for example the data which you are looking to update your store with.
What ends up happening is in your component when you want to update the state (say from an input), you use the methods you wired up with mapDispatchToProps to dispatch an action with the relevant data (the updated input value), and then write a reducer which responds to the action and updates the relevant part of your store. Redux will then trigger a re-render in your component and it will pull the updated data from the store.
Async actions are handled a bit differently of course (which is what your API calls will be), because they typically have several changes to state being spread out over time. Usually something like:
Start the API request. (Dispatch an action to..) Set a flag somewhere in your state indicating the API request is 'in transit'.
Receive the response. (Dispatch again to..) Set a flag somewhere saying the request is finished, and if it was successful, store your response data somewhere. If it was an error, store the error (or just the fact there was an error).
I would recommend redux-thunk for this purpose, since at least for me it is the easiest to understand and work with out of all the various libraries and methods to handle async with redux. Instead of dispatching an object which contains the data describing the action, you instead dispatch a function, which means you can write any kind of logic you would like, allowing you to dispatch several times for example. This makes it easy to do the steps outlined above.. simply have a function which calls your API, and in the callback (or .then() method) you dispatch an action with the result.
In redux, when we need to access a state property that is not directly related to a componentProps but is triggered from this component (ie: a grand parent component id that is stored in the state) should we try to pass this part of the state to action creators from the component that triggers the event ie:
onClick={() => doSomething(grandParentId)}
----
function doSomething(grandParentId) {
console.log(grandParentId)
}
or should we get the data from getState with redux-thunk?
onClick={doSomething}
----
function doSomething() {
return (dispatch, getState) => {
console.log(getState().grandParentId)
}
}
I know that the answer sounds like the first approach is more proper, but I somehow feel that getState with redux-thunk can lead to bad practices and I would like to know when I should use it or not.
If you already have that information in your component, I don't see why not passing it along to your action creator. The flow of data is much easier to follow this way, in my opinion.
I would take advantage of getState() only when I need to access part of the state tree that I wasn't able to pass via action creator.
If the data is already in the redux state, then I would access it with getState. Fewer arguments being passed around makes it simpler IMO.
After implementing Flux, it was easy to see that it is in fact unidirectional.
Flux recap: The View takes in user actions, which get sent to and interpreted by Action Creators, which get sent to the Dispatcher, which get broadcast to the Stores, which eventually trigger an update back in the View.
With Redux, although I've only read a handful of blog posts on it, it seems as though the Action Creators are in fact returning the formatted action object back to the View that fired off the action in the first place.
See: https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6#.3hquuzljm
So the question is this: Does having the Action Creator by design return to the View disrupt the very notion of uni-directional workflow?
I haven't implemented Redux just yet, so perhaps I'm missing a step, but this sticks out to me as a big difference between Flux and Redux.
I think you're putting too much emphasis on what action creators are.
An action creator is literally just this:
function login(username, password) {
return { type: 'LOGIN', payload: { username, password };
}
These two things are identical:
class Component extends React.Component {
...
render() {
const { username, password } = this.props;
return (
...
dispatch({ type: 'LOGIN', payload: { username, password });
// or
dispatch(login(username, password));
...
);
}
}
And then if you user react-redux's connect, it can look like
class Component extends React.Component {
...
render() {
const { username, password } = this.props;
return (
...
login(username, password)); // connect binds the AC to dispatch for convenience.
...
);
}
}
Action creators are not a large abstraction. they're just a special name for a function that helps makes actions to keep your code DRY. They don't do anything to change the unidirectional model.
The data flow in Flux and Redux is pretty much the same, you just don't have the Dispatcher and callbacks, because the actions operate on store. (Compare it with this flux diagram.)
The Redux documentation has a great chapter on this topic:
Redux architecture revolves around a strict unidirectional data
flow.
This means that all data in an application follows the same lifecycle
pattern, making the logic of your app more predictable and easier to
understand. It also encourages data normalization, so that you don't
end up with multiple, independent copies of the same data that are
unaware of one another.
If you're still not convinced, read
Motivation and The Case for
Flux
for a compelling argument in favor of unidirectional data flow.
Although Redux is not exactly Flux, it
shares the same key benefits.
The data lifecycle in any Redux app follows these 4 steps:
You call store.dispatch(action).
The Redux store calls the reducer function you gave it.
The root reducer may combine the output of multiple reducers into a single state tree.
The Redux store saves the complete state tree returned by the root reducer.
Does having the Action Creator by design return to the View disrupt the very notion of uni-directional workflow?
TLDR:
The action creator in redux returns the action without calling dispatch. Correct.
Does this break unidirectional workflow? No.
After receiving the action from the action creator, it can be forwarded to the store by calling the dispatch() function with the returned action as parameter. Thus the data flow is still unidirectional.
Details:
1
In traditional Flux implementations, action creators often trigger a dispatch when invoked, like so:
function addTodoWithDispatch(text) {
const action = {
type: ADD_TODO,
text
}
dispatch(action)
}
By contrast, in Redux action creators are pure functions with zero side-effects. They simply return an action:
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
This makes them more portable and easier to test. To actually initiate a dispatch, pass the result to the dispatch() function:
dispatch(addTodo(text))
dispatch(completeTodo(index))
http://rackt.org/redux/docs/basics/Actions.html
2
As shown in https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6#.e9br039va
The view requests an action. The action creator formats it and returns it.
The action is either dispatched automatically (if bindActionCreators() was used in setup), or the view dispatches the action.
The store receives the action.
So therefore the dataflow is still unidirectional.