So in plain react, setState triggers the render method. Now, about Redux, let's say I fire an action, the state returned from the reducer(s) is totally new, so it sounds like every component should get updated, though I know it's not true because when I debug my app I can see it, plus it sounds like awful performance. So my question is, does redux look in each component what state slice it needs? in mapStateToProps? and generally how does Redux decide when to call the render method?
I know Redux has optimizations in it... thanks...
A Redux store offers a subscribe() method. When you use connect(), it generates wrapper components that automatically subscribe to the store when they are created. Each individual connected component is a separate subscriber.
When an action is dispatched, the store runs all the subscription callbacks. The wrapper components then call store.getState(). As a first check, if the root state object is === identical to the previous one, the component will stop there. (This is one reason why direct mutation of state in a reducer is bad.)
The next step is to call the provided mapState function and pass in the current state object. The wrapper component then compares the contents of the object returned from mapState against the contents of the object returned last time, using a shallow equality comparison check. If any of the contents have changed based on that shallow check, then the component knows it needs to re-render the "real" component with the new data. (Again, the "shallow" check is another reason why mutation is bad.)
Related
What would be the best way, while following React best practices, to define globals that changes according to props provided by a redux state.
For example, let's say I have a metric or imperial toggle value that's dispatched into a persisted redux state. Is it possible to call for a constant that changes its value based on the toggle without providing the current state of the toggle by passing it via props?
My current implementation is doing
globals.distance(this.props.metric)
where this.props.metric is mapped from the redux state to the component's props. I want to somehow implement subscription to the redux state from within the globals file, so I can just do constants.distance. I've tried importing getState() as well as subscribing to changes in the redux state, but the problems there is that getState() only runs once when the file gets loaded, and I can't seem export constants subscribed to the state since the function returns an unsubscribe function. I can also create a Component and subscribe that to the redux state, but I can't seem to find an easy way to export constants from within a Component state either.
Well, I couldn't find a solution I liked, so I made my own. It's a thin wrapper to connect functions to the Redux store and return a value.
In my React app, I have a redux reducer which sends a List as props to my component.
I copy the prop to local state, and show as drop down. User changes the Dropdown so my local state changes.
On click of cancel , I am calling redux Toastr which triggers a method to reset my state with the original props.list. But for some reason the props.list also changed similar to my state change. With my knowledge i thought props passed to the componeent will not be changed until again i call action creator.
Anyone faced similar issue? or i am doing something wrong
Sorry for not posting the code, which I will prepare a demo if needed. Thanks!
What you are doing is an anti-pattern as you are breaking react's rule of single source of truth. You can't hold state internally in a component that is tied up with your redux state and expect it work smoothly. A similar issue of breaking the single source of truth arise when using solely React without Redux and when you try to pass props as state. In this case there is a new lifecycle hook static getDerivedStateFromProps but even this hook is advised to be used sparsely you can read about it here. So if your intent is to reset state back to it's original value, you can either:
Use static getDerivedStateFromProps (which is reserved mostly for UI)
Use a key prop which will reset you back to your initial state
Use a memoization helper such as memoize-one
In my React/Redux application, I trigger an API call in the index.js (The entry point) as I want the data to be available even before user navigates to the component.
Actually, this approach helps to reduce the user's wait time as we'll have the data loaded before user reaches the component.
In our use-case, we need to initialize the component state with the props (props are passed from store). I faced below issue in the approach.
case 1: Let's assume user has reached the component before the API
returned that data. In this case, that particular store object will be
empty and the props (which is passed from react-redux connect
wrapper) will be undefined/null. So I need to render "Loading.."
message. When the data arrives, we initialize the component state
using setState() in componentWillRecieveProps().
case 2: If data has arrived before user navigates to the component.
For this case, We can either use constructor() or componentWillMount() to initialize the state from props, as props is having the data. I can't rely only on componentWillRecieveProps() as, it'll not execute during the initialization life cycle.
So I ended up doing the same initialization steps in 2 places as component should handle both cases. Is this a right approach? or Is there any better approach to avoid these kind of DRY code.
Thanks in Advance!!!
I've had the same exact issue that you outlined and also wanted a DRY solution, so I ended up creating a HOC (higher order component) for this purpose:
https://www.npmjs.com/package/react-prepare-wrapper
It lets you assign a single function to be called for both componentWillMount and componentWillReceiveProps.
I'm not sure it is default behaviour or redux or something else but i found that on dispatching an action, this action traverse through all reducers(that's ok) but it also invoke the connect listener of every reducer that further resulting rendering of its component. This means on every dispatch, all component inside app state tree get rendered. Is this intentionally done by redux or i've done something wronge.
Help me out to clarify this things.
In Redux , your state is global and handled by the redux, whenever you dispatch an action , you are just setting the global state. Your container comonents will receive the new state and reducer would work on them but your components wont be rerendered since previous state and next state would be same.
Only those components would be rendered whose mapStatetoProps result in a different result
This behavior is totally fine. See the React Docs for their virtual DOM concept:
React makes use of a virtual DOM, which is a descriptor of a DOM subtree rendered in the browser. This parallel representation allows React to avoid creating DOM nodes and accessing existing ones, which is slower than operations on JavaScript objects. When a component's props or state change, React decides whether an actual DOM update is necessary by constructing a new virtual DOM and comparing it to the old one. Only in the case they are not equal, will React reconcile the DOM, applying as few mutations as possible.
So you don't have to worry that every component will get re-rendered every time you dispatch an action.
I've created a small ReactJS dashboard with the help of SocketIO for live updates. Even though I have the dashboard updating, it bugs me that I'm not quite sure if I did it correctly.
What bugs me the most is the Props in getInitialState as anti-pattern post. I've created a dashboard that gets live updates from a server, requiring no user interaction beyond loading the page. From what I've read, this.state should contain things that will determine whether the component should be re-rendered, and this.props.... I don't know yet.
However, when you initially call React.render(<MyComponent />, ...), you can only pass props. In my case, I get all data from the server, so the initial props just end up in this.state anyway. So all of my components have something like this:
getInitialState: function() {
return {
progress: this.props.progress,
latest_update: this.props.latest_update,
nearest_center: this.props.nearest_center
}
}
Which, unless I've misinterpreted the aforementioned blog post, is an anti-pattern. But I see no other way of injecting the state into the Component, and I don't understand why it's an anti-pattern unless I relabel all of my props to prepend initial on them. If anything, I feel like that's an anti-pattern because now I have to keep track of more variables than I did before (those prepended with initial and those without).
Disclaimer: When I answered this question I was learning / trying to
implement vanilla Flux and I was a bit skeptic about it. Later on I
migrated everything to Redux. So, an advice: Just go with Redux or
MobX. Chances are you won't even need the answer to this question
anymore (except for the science).
Passing the intial state to a component as a prop is an anti-pattern because the getInitialState method is only called the first time the component renders. Meaning that, if you re-render that component passing a different value as a prop, the component will not react accordingly, because the component will keep the state from the first time it was rendered. It's very error prone.
And here is what you should do:
Try to make your components as stateless as possible. Stateless components are easier to test because they render an output based on an input. Simple like that.
But hey.. my components data change.. I can't make them stateless
Yes you can, for most of them. In order to do that, select an outer component to be the state holder. Using your example, you could create a Dashboard component that contains the data, and a Widget component that is completely stateless. The Dashboard is responsible for getting all the data and then rendering multiple Widgets that receive everything they need through props.
But my widgets have some state.. the user can configure them. How do I make them stateless?
Your Widget can expose events that, when handled, cause the state contained in Dashboard to change, causing every Widget to be rerendered. You create "events" in your Widget by having props that receive a function.
Ok, so now, Dashboard keeps the state, but how do I pass the initial state to it?
You have two options. The most recomended one, is that you make an Ajax call in the Dashboard getInitialState method to get the initial state from the server. You can also use Flux, which is a more sophisticated way for managing data. Flux is more of a pattern, rather than an implementation. You can use pure Flux with the Facebook's implementation of the Dispatcher, but you can use third-party implementations like Redux, Alt or Fluxxor.
Alternatively, you can pass this initial state as a prop to the Dashboard, explicitly declaring that this is just the initial state.. like initialData, for instance. If you choose this path, though, you can't pass a different initial state to it aftwards, because it will "remember" the state after the first render.
OBS
You are not quite right in your definitions.
State is used to store mutable data, that is, data that is going to change during the component life-cycle. Changes in the state should be made through the setState method and will cause the component to re-render.
Props are used to pass in imutable data to the components. They should not change during the component life-cycle. Components that only use props are stateless.
This is a relevant source on the "how to pass the initial state to components".