I build an app with pure React. Its primary page shows multiple movies (user added). Users can vote and comment on every movie listed. I have a MovieCard component, which is responsible for a single movie. Then in Home I render an array of MovieCard components. Next I decided to implement a call to foreign movie database API, to get relevant movie information. It worked fine. Then I decided to refactor it in Flux pattern, using Alt and following the guidelines of this tutorial. Everything worked well, until I got the the MovieCard. In there, inside componentDidMount I call the action, which sends ajax and retrieves a poster url. And I got this error:
Uncaught Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.
First, I read this topic. The author of the answer claimed that having each component handles its actions is bad design pattern and is what cause the error. His solution would be to handle ALL actions and stores from the top-most component (App) and pass down as props. My thoughts:
I don't like it, because it means passing down a LOT of data and callbacks and my App.js is going to be overly long and ugly and clouded. Lastly I don't know how this is the "React way", where each component is standalone and can work on it's own, out of App context.
I don't see how it fixes the problem. I want my MovieCard to fire action to get data inside componentDidMount. So even if App is handling the actions, when multiple movies are shown, then multiple actions will be fired, again.
Then I read this topic. I don't think I am using actions wrong. Here is my action:
getMoviePoster(movieName) {
TMDB.getMoviePoster(movieName)
.then(data => this.getMoviePosterSuccess(data))
.catch(err => this.getMoviePosterFail(err));
return true;
}
I followed the pattern, showed in the guide I linked above. There were no errors there.
I read through some other topics, there was something about waitFor token, but I couldn't make much of it. Can anyone please shed some light on the issue, because I am in sort of a hurry. I need it done by Tuesday, and I may have a major refactoring on my hands now... I really don't understand what is the problem here. I know Flux is unidirectional, but I thought that's for one component. I'm not guru here, but I can't really see unidirectional flow for the entire application (even this small). I don't see what's the problem of having multiple instances of the same component doing stuff simultaneously.
EDIT: I read again through Alt docs and getting started guide I don't see anything against multiple dispatches. I read about AltContainer, but i'd like to keep my app and structure simple at the moment. In my action constructor I use this.generateActions; in store - this.bindActions(MovieCardActions), which works very well. Except when I have multiple components mounted at the same time. I still can't get the problem.
Related
As I understand if we make a request by using redux-saga, the response is going to be stored into the redux store. But it is a bad practice to store all the stuff in the redux store. So is it ok to create separate module with API requests and use them when you do not want to put response in redux store ?
I would appreciate articles on this topic (couldn't find it on my own).
Making requests in a file is totally fine in some cases. Just do it carefully in order to prevent interfering with app performance (if a component is rendered multiple time and you need just one request do it in the parent, etc.)
As for articles I couldn't find any to exactly this topic, but react docs have an example of api calls (please ignore the class one and check the hook example bellow). Now I would recommend you too use a service for this to abstract the fetch logic, but the idea is there and, while this cases shouldn't happen a lot, I don't see any reason to avoid this if you only need some data in a specific component and there is no use in saving it to the state .
EDIT:
You have runSaga which according to docs
Allows starting sagas outside the Redux middleware environment. Useful if you want to connect a Saga to external input/output, other than store actions.
So you don't really need another library. Regarding the architectural problem this is what I was trying to emphasis with the react docs. I don't know if there are any articles in this direction (on a quick search I couldn't find any either), but I think for most of the data you want a reducer to prevent unnecessary reloading of the same data.
However there isn't anything suggesting not to do it as far as I know (neither in docs, articles, etc.). Moreover, in some cases, this can a good thing, and the fact that no docs suggest otherwise is a prove in this direction IMO.
PS: Also a discussion about using saga without redux here
I have a question about calling API in react.
Example in the website. We have a lot of page. Each page has a lot of components. And each component has its own data need to get in server.
I see we have two way to call API is:
First. We call all API of each page in a root of each page then set the data to state. After that, we pass data to children Component.
Second. In each component, we call its API to get its data then set the data to component 's state.
So which is better. I need an explain about that.
Thanks you,
There are many ways to pass Data through out the components.
If the application is small and there are small number of child components you can go by making calls in Root folder.
There would be some components that always doesn't render and only rendered based on specific conditions at this point you can go by making calls from that component.
Using redux and redux thunk is always an option if the data is needed in many components and data can be accessed at any point of time.
As noted in the previous answers/comments you could do either one of these. If you plan to use redux it might be easier to chain the api calls in a single action w/ thunk that gets ran on main component load.
Context or Redux would do you well so you don't have to pass tons of data through prop levels.(prop drilling)
I would suggest Redux, IMO context gets too cluttered and by the time you've properly atomized your code to clean up everything you may as well have just went through the overhead of adding redux.
What you should ask yourself is-
Does it make sense to have all this data load at the same time?
Is it appropriate for some api calls to be made from the components that will use them?
You have creative license to do what works best for you.
I am using Alt library for Flux architecture implementation. I have a component, which displays information about movie. Now I am stuck in a situation where I need to render array of such components under my home page. The problem is that each of these components calls ajax request inside componentDidMount. So when I have more then one - I get:
Uncaught Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.
I wrote a longer post here, this is a more succinct version. I simply cannot understand the nature of the problem.
EDIT: Clarifying with an image. Basically I get the poster for the first component and on the second it throws an error and stops working:
After a few days of researching and some help, I got my problem solved.
The simple answer is: You cannot. This is considered not only bad practice, but Flux anti-pattern, an as such is restricted by the standard Facebook dispatcher (which is what Alt uses). The correct way would be to handle actions and stores from the top-most component. You could also use custom dispatcher, but I guess it's not restricted for no reason.
In my case that was the HomePage component and what I did was to fire single action, which got all movie posters and then passed that information down with props.
As far as I understand the best practice would be to have a container component at the top-most, which is only responsible for actions and stores. Then inside the container - a view component (one or more), which are responsible for rendering the UI and data. I find this article well informative.
I'm codesplitting my React+Redux application as described by Dan Abramov here, and everything appears to work fine. However, I'm also rendering the application on the server. This results in console error documented by this answer. However, I'm not attempting to clean up old stateāI'm loading the state as the server computed it. The problem is the state from the server gets loaded into the global state before the codesplitted modules are loaded.
If I understand Dan properly, the error is just a warning, and everything appears to function correctly, but it's really not a pleasant development workflow to see errors on nearly every page load.
Is there anything I can/should be doing differently with my codesplitting code to alleviate this? It's nearly verbatim to Dan's example.
The answer was kind of obvious when it hit me. Just like you send the redux state to the client, you need to tell the client which optional reducers need to be included into the combined reducer on creation.
Based on Dan Abramov's work in the linked answer in my question, I changed store.asyncReducers into an array of paths instead of a map of objects. Then I was able to serialize this array and send it to the front-end where it was able to require the async reducers that the server-side render used.
I realize this question has been asked before and this topic has been widely discussed in the Redux community, but I have not seen it approached by this angle: Error messages.
In most examples using React + Redux + some middleware (redux-promise and redux-thunk), external api calls are done inside the action creator. The result of the API call then affects the application state with a success case or error case.
My counter-argument:
The main interested party in the results of an API call is a component, particularly because it's the one that has to often show an error message to the user. Error messages are best set as component state. It's easier to "clean up" on componentWillMount. No need to create an action just to clean up an application level error state.
All API call's should be made from a component and it should decide what action creator to call. Action creators then become JUST that, functions that return objects. No side-effects in them.
Again, I stress that this "take" is based on the fact that most of the time, a component will need to handle error messages anyways. So why not call the api and deal with the error right there? Things go ok, call an action creator. Things go bad, show an error. Also, I don't think there will be duplication of API calls across the application. After all, React tries to enforce modularization and top-down flow of data. Two different components really shouldn't be calling the same api. They could call the same action creator though and that's fine. Think sign up and sign in. Different api endpoints. Same final state (authenticated: true)
Anyway, this is my view on it. I'm hoping that someone with more experience will answer if API calls inside components are a good idea. Thank you.
EDIT: Just created this post on medium, which hopefully explains my argument better
Kind of too open ended to come up with a "solution" but here's a short answer.
First off, what do you mean it's easier to clean up on componentWillMount? Many times api calls are done on an already mounted component like a sign up or login component. The API call happens when the button is clicked, not when it's mounted.
Also, the main reason why API calls are done outside React components (assuming you have a data handling framework like redux) is that the library is used as a View layer. A component renders HTML that declaratively reflects the state of your application. When a login API call fails to authenticate, the application state is what changes, and as a result the View. If you start to handle API responses in your component, you may run into issues with out of sync state.
For example, the user logs in 10 times with the wrong credentials and gets "locked out". How do you handle that error? You'll likely add some logic to handle those errors. And what if other parts of the app need to react to this error? Now you start to fire actions based on those errors and essentially go back to making your API calls entirely from an action creator, which happens to live in your component.
Now, this mostly applies to large applications. It's perfectly reasonable to handle API calls in a component if the application is small enough and state management frameworks like redux just add bloat. If it's a large application, however, I still highly recommend keeping API logic in the action creators.