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.
Related
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 currently have a React app that I'd like to use SSR with. All but one component is pretty much static content, making SSR super easy. Everything but the component is rendered just fine and well right now.
My question is how do I go about rendering this component that needs to first get data? Because it's a complex SVG that gets rendered my line of thinking is that having it "update" once data comes in is a bad move, and it would be better for it to just not exist in the absence of data (with an error message).
So here's my plan: I can add a prop to the component to pass in data from a parent rather than keep it as internal state only. So if data is passed, no fetch request in the component is necessary. From there what I can do is take the static bundle output of the app and, when the page is requested, the server will request the proper data just as the component would. Once the data is received, the server can grab the component from the bundle with regex, add the data as a prop, render the component, and stick it back in with the rest of the already rendered static content.
Is this the correct way to do this? It feels a bit complicated, but that might just be how it's done. I'm not sure.
Your intuitions are correct. In current React (17.0), it's quite cumbersome to do SSR with data-fetching inside components.
What needs to be achieved on a conceptual level is that all data dependencies need to be known upfront, ie. before calling ReactDOM's render. This way, one can access the data in a synchronous manner which allows to do one-pass render on a server.
I don't quite follow your idea to "grap the component from the bundle with regex". One way of solving the data dependency problem is to inject the data into React tree from the root component (ie. ReactDOM.renderToString(<App componentStaticData={data} />)) and make the data-dependent component aware of the fact that it can just grab the data from there instead of doing (asynchronous) call. It's important to note that useEffects are not executed on the server.
Another idea to grab all the data dependencies is to do two-pass render. First one is used as a way to collect all resources used, then we await their completion and inject them as static data into send pass.
Third way is to use one of the React frameworks that provide SSR out of the box. You can have a look at (among many others) Next.js or Gatsby. Depending on your setup, this might be easiest or the hardest way to achieve SSR.
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.
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.
I am using react-router with the flux architecture (facebook's flux implementation).
Currently in my system I have route that says "chat/:topic".
When the user is entering this component, I am creating a subscription (using action creation, on componentWillMount) to a websocket server, and I am removing the subscription on componentWillUnmount.
When the user is moving to another route the whole workflow works alright - because react-router is unmounting my component.
When I transition inside my route (from "chat/games" to "chat/tv"), the component isn't mounted and I need to clear my state of the components.
I read about different actions that I can take and this on transition to dispatch an action "TRANSITION" and every relevant store will clear it's store.
In my opinion, this kind of action - is wrong, it couples my stores and my router.
How would you solve this problem? Is this an issue that I should raise to react-router and ask them to unmount inside my route?
I found the answer thanks to gaearon (https://github.com/gaearon/),
Use a store to keep the selected topic and ask the messages store for messages, in flux you shouldn't remove anything from your store, unless you need it for a performance reasons.
In my application, I must remove the messages (since they are large objects) and clear my subscriptions (to reduce the load on the server).
In order to achieve this there were three solutions that I found:
Use componentWillReceiveProps and check if the params are changed, if the params are changed - do whatever you need in order to clear the store - for example call ActionCreator and reset the state.
Send a dispose payload in transition (inside Router.run) which will tell all the stores to clear themselves.
last solution (which I used) making sure that my components are unmounted - Why? It is too error prone to clear the state on componentWillReceiveProps/dispose and it is clearer to just ensure the components are unmounted.
Details on how to achieve this:
https://github.com/rackt/react-router/issues/292
https://github.com/rackt/react-router/issues/496#issuecomment-64152941
I believe that compomentWillReceiveProps might solve your issue. This is what react router uses to pass stuff to you.
As far as I know, you need to use both componentWillReceiveProps AND componentDid/WillMount to catch the initial render also.
I'm anxiously awaiting the react-router 1.0 release (this weekend?) in hopes that there is a more elegant way to do this.