React / Flux, setting multiple states - reactjs

I'm new to react and I am using states for my data models.
I have a menu that shows a user's profile picture through state. It's state because a user can change his profile picture.
I'd like the Menu to slide in from the left, initially hidden. Hence I'd like to add the Menu's open/close status as a state as well.
I'm using the standard Flux pattern.
Here is the relevant code:
Menu.js
_onChange:function(){
this.setState({
opened: MenuStore.getMenuState(),
profilePicUrl: MenuStore.getUserPic()
})
},
componentDidMount:function(){
MenuStore.addChangeListener(this._onChange)
}
MenuStore.js
MenuStore = assign({},EventEmitter.prototype,{
emitChange: function() {
this.emit(CHANGE_EVENT);
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
...(rest of class not shown)
})
MenuStore.dispatchToken = Dispatcher.register(function(action) {
switch(action.type) {
case ActionTypes.RECEIVE_USER_DATA:
_userData = action.details;
MenuStore.emitChange();
break;
case ActionTypes.TOGGLE_MENU:
_opened = !_opened;
MenuStore.emitChange();
break;
default:
// do nothing
}
});
Nav.js
toggleMenu:function(){
Actions.toggleMenu(); //in my actions.js it dispatches TOGGLE_MENU
}
render:function(){
return <div onClick={this.toggleMenu}>My Nav button</div>
}
I guess what I find wierd, is that I am setting the state of the User's profile picture without it having changed. Is this the correct way of doing things? Or should I emit separate change events and hence use separate callbacks, so that I set the states separately?
A related question is whether React will care if I set the state of something that hasn't changed. I.e does the diffing algo ignore the user's profile pic since it hasn't changed and therefore has no effect on React? OR does the fact that I've set the state in react, implicitly tell React that 'something has changed'?
Which is the correct React / Flux pattern? Set all the states in one callback? or set all the states separately?

There are a few things I learnt working with React and Flux that, I hope, can improve your approach:
Ship the whole state with the emitted event
In your example, you are emitting an event, and then the component is asking for data:
_onChange:function(){
this.setState({
opened: MenuStore.getMenuState(),
profilePicUrl: MenuStore.getUserPic()
})
}
I would suggest you move to a different pattern, where the store sends the whole state, no matter the event, every time it emits an event:
case ActionTypes.TOGGLE_MENU:
_opened = !_opened;
MenuStore.emitChange({opened: _opened, /* [...] */});
break;
Then in your component:
_onChange:function(snapshot){
this.setState({
opened: snapshot.opened,
profilePicUrl: snapshot.profilePicUrl
})
}
In this way your store scales up, no matter the amount of data you want to keep in the store.
Consider using Immutable.js or shipped immutability helpers
A related question is whether React will care if I set the state of something that hasn't changed.
React will trigger a virtual re-render in the virtual DOM. Then, it will execute the diff algorithm. As nothing has changed, nothing will be re-rendered.
You can avoid this by overriding shouldComponentUpdate:
Invoked before rendering when new props or state are being received. This method is not called for the initial render or when forceUpdate is used.
Use this as an opportunity to return false when you're certain that the transition to the new props and state will not require a component update.
I would strongly suggest that you start using either the immutability helpers, or Immutable.js. In this way it becomes easier to manage the whole re-rendering process, as it becomes trivial to understand when something has really changed.
Also, bear in mind that React is extremely fast. Unless you have > 100 components listening for changes, sometimes it is better to have some wasted re-render cycle, instead of writing a convoluted shouldComponentUpdate.
Code readability vs performance
I guess what I find weird, is that I am setting the state of the User's profile picture without it having changed.
This is really a trade off. If you have ~100 components listening to the same store, some of them interested in just one event, others in another one, then it would be the case to have different events (you would continue to send anyway the whole snapshot). Otherwise, I would suggest to keep your code simple: just publish one event.
Flux comes in different flavors
There are a few libraries now that implement ideas taken from Flux. I would suggest you have a look at Reflux and, especially, Redux.

Related

When 'action'/'runInAction' is really needed in mobx react

Can someone explain to me what is the real difference and why both of the example here are working the same:
1) Change a state of observable via action/runInAction inside the store file:
colorStore file:
#observable
color='red'
#action
setColor(){
this.color='blue'
}
2)Change the state via the component itself (which assumed to be bad practice):
React Component file:
onClick = () => this.props.colorStore.color='blue' //still working...
Mobx action is doing the batching, similarly to how ReactJS batches multiple changes.
When you use reactjs click handler react is automatically batching changes that happen inside it, so you will not see component rendering multiple times, however, if you call setColor from some other event, let's say after loading some data, and have multiple calls to change the observable inside the setColor that will trigger the observer three times, and the component will be rendered three times.
When you wrap your function with #action decorator or you use runInAction function, only the last value will be used (green in the code below) and the component will be rendered only once.
setColor(){
// this will render three times
this.color='blue'
this.color='red'
this.color='green'
}
vanilla mobx example that reacts only once:
import { runInAction, observable, reaction, toJS } from "mobx";
const test = observable({
name: "sonic",
nick: "speed demon"
});
// console.log will be called only once with the value "name: 3"
reaction(
() => toJS(test),
data => console.log(`name: ${data.name}`)
);
runInAction(() => {
test.name = "1";
test.name = "2";
test.name = "3";
});
view on codesandbox
Also check out the discussion on the github repo: is #action really necessary?
The difference is more related to conventions and writing clean maintainable code than in the behavior of the program. So:
You are mutating store data in your UI component. This is not correct, because the UI should only visualize data and handle for example user actions which are then translated to certain action in the data store(in your case updating a color). It's store responsibility to manage the state of this data
All observable data is considered a good practice to be mutated only in actions. Why? Because the single source of mutating data is clear - only in actions and only in a data layer(the store). The code is less prone to errors and unclear state management and the great benefit is that you can enforce your application not to build if you turn on use strict mode

Recording redux navigation state in model state?

I've structured my redux application such that my data models are handled on separate branches of the state tree.
{concerts, venues}
I've also used react-navigation-redux-helpers to put my navigation state into the tree:
{concerts, venues, nav}
However, I want to record information about the visibility state of a particular model. When the ConcertScreen is shown, I want to know when a user's looking/stops looking at a particular concert ID (and letting the server know), with the eventual goal of measuring how long a particular concert ID was visible on screen.
I've done this by adding branches for Navigation/NAVIGATE, Navigation/RESET, and Navigation/BACK to the concerts reducer, and setting visible: true on the appropriate object under concerts.
This has been error prone, since the navigation state can be modified by actions other than these specific actions. (A logout action handled directly by the nav reducer, for example.)
I see two alternatives, both not ideal:
Use props.navigation.addListener to listen to focus and blur events on the ConcertScreen, trigger custom concertFocused/concertBlurred actions, and handle those in my concert reducer instead of the Navigation/* actions.
Create a selector that computes the currently visible concert from the nav state and refactor the business logic that expects concert.visible as input to use the selector instead.
The problem with 1 seems to be that it's adding overhead to the event loop, all the extra actions flying around means extra rendering overhead.
2 avoids the extra actions, but it seems like a lot of refactoring for not a whole lot of gain, and it means I have to move business logic out of the concert reducer and put it elsewhere.
Say I use option 2. I add a middleware that, on any action, applies the selector to state.nav and from that computes what Concert is currently displayed. If I wanted to measure duration, how would I store start/end time? Fire a new action with that added data so the concert reducer catches it? That just seems like option 1 with extra steps.
I could also have this middleware add a field to every action indicating the concert display state, and have the concert reducer handle it in the default/fallthrough case. Do people do that?
I would approach your use-case in such a way, that I will get the best of both solutions.
First of all, having many actions dispatched you're worried about rendering overhead. Using a selector library, let's say reselect, the library memoization will prevent unnecessarily components rerendering.
Later on, if I understand you correctly, your goal is to let the server know the visibility status of an item (concert) and eventually its visible time. If your goal is notifying the server only, without letting know the rest app's front-end users, why do you want to keep tracking it in Redux too? You can skip the Redux part and send updates to the server only.
Let's assume, that you need Redux for the tracking. You can try on your way structuring the Store, as you already mentioned, adding the visible flag to each object in the Redux store. But if your items' structure is bigger enough and it's costly to copy and update the object each time when changing the visible flag, you can consider creating a dedicated Store branch and reducer, that will be responsible only for the tracking needs. Something like that:
tracking : {
concerts: {
1: { visible: true, time: 10 }
}
}
Now, updating an item's flag, only the above tiny structure has to be copied and modified. Even, you can make it smaller and more specific for a certain item type (trackingConcerts).
* Keep in mind, it's on your own to decide whether or not such a dedicated Store branch will improve the performance, because we don't know your detailed architecture and Store specifics.
Continuing with the solutions ...
Relying on navigation actions + middleware is error prone, as you mentioned. What about the use-case you have a general page of components (i.e. navigation action with generic name will be dispatched), but you render there one of your items (concert)? Also rendering an item, would be always coupled with modifying the mapping logic in your middleware or wherever place you track the items by action name. Another tricky case is when you render different type of items (concerts, venues) on exactly one page. How will you differ and track the items, considering you have only 1 navigation item? Also in a such setup, I don't see a straightforward way for handling an item visible time.
About the selectors as a solution - they can be only a small part of the solution. The selector is responsible for selecting and managing derived state. Nothing more.
Show me the code, please.
I would create a wrapper component around react on screen (or any similar library that tracks component visibility) and implement only the tracking visible time of the component.
The wrapper will trigger callbacks when the component visibility state is changed and a callback on componentDidUnmount including the visible time.
That's all! Now you can attach handlers on these callbacks and you can update your Redux and/or notify the server for the visibility changes, without relying on any navigation actions and middlewares.
Usage:
const App = () => (
<Tracking
onVisibilityChange={isVisible => {}}
onUnmount={visibleSeconds => {}}
>
<Concert id={1} />
</Tracking>
)
Tracking Wrapper:
import TrackVisibility from 'react-on-screen'
const Tracking = ({ children, libraryProps, ...rest }) => (
<TrackVisibility {...libraryProps}>
<TrackingCore {...rest}>
{children}
</TrackingCore>
</TrackVisibility>
)
TrackingCore, our custom tracking logic:
class TrackingCore extends React.Component {
constructor (props) {
super(props)
this.state = {
visibleSeconds: 0,
interval: null
}
}
componentDidMount() {
this.track()
}
componentWillReceiveProps (nextProps) {
this.track(nextProps)
}
componentDidUnmount() {
const { visibleSeconds, interval } = this.state
const { onUnmount } = this.props
onUnmount(visibleSeconds)
clearInterval(interval)
}
track (nextProps) {
const { isVisible, onVisibilityChange } = this.props
const { visibleSeconds, interval } = this.state
const hasVisibilityChanged = (isVisible !== nextProps.isVisible) || !nextProps
const isVisibleValue = nextProps ? nextProps.isVisible : isVisible
// On visibility change, invoke the callback prop
if (hasVisibilityChanged) {
onVisibilityChange(isVisibleValue)
// If it becomes visible, start counting the `visibleSeconds`
if (isVisibleValue) {
this.setState({
interval: setInterval(() => this.setState({
visibleSeconds: visibleSeconds + 1
}), 1000)
})
} else {
clearInterval(interval)
}
}
}
render () {
return this.props.children
}
}

Trigger react component render from rxjs observable subscription (skip the initial render)

I am trying to use redux state store in a redux app using a rxjs obserable wrapper. Source tutorial
I first tried this approach when I switched from ngrx to redux in angular. Now I’m using this pattern in a react app. However, I have a bit of an issue. When I subscribe to some state store stream I use setState(foo) to store the value in the component. This in turn triggers a new render cycle. I’d like to have one render cycle per component instead of 2 or more. 
I’ve been trying to inhibit the initial rendering of the component in order to have it triggered first and only once by the state store subscription. When you have multiple nested components and multiple subscriptions they tend to create wasteful renderings just to do the app init. I know that React does a great job of optimising for multiple renderings but still I find that keeping an eye on the rendering cycles is healthy for avoiding subtle bugs.
Any recommendation on how to trigger the first rendering from the state store subscription?
app.module.tsx
private subscribeToAppSettings() {
DEBUG.cmp && debug('Subscribe appSettings$');
appSettings$().pipe(
skip(1), // For REST api calls I skip the initial state
takeUntil(this.destroyed$),
)
.subscribe(settings => {
DEBUG.subscribe && debug('==> Observe appSettings$', [settings]);
this.setState({ settings });
});
}
As you can see AppModule and everything else is rendered twice because of this subscription. This is a filtered set of logs, showcasing when the app is running the render() methods. Just the init stage, no user interactions.
After reviewing the entire architecture again I figured that I need to manually set the initial state in the components. Now, the initial rendering is doing the useful work, and the second rendering will be ignored by the react change detection.
I still have the extra rendering cycles. However, I see that this is the state of affairs with change detection. A lot of things trigger a second rendering: the init, the router, the event handlers, the observables. As long as React is using the virtual dom for change detection to weed out values that do not actually change, there should be no real impact on performance. As they say: I'm barking at the wrong tree.
state.service.tsx
/** Access state changes as an observable stream */
export const store$ = new Observable<AppState>(observer => {
// All state store observable use `distinctUntilChanged()` operator.
// Without this initial state, `distinctUntilChanged()` will be unable to compare previous and current state.
// As a result, the webapi observable will miss the first response fron the server.
observer.next(appInitialState);
let appState: AppState;
store.subscribe( () => {
appState = store.getState();
observer.next(appState);
});
})
app.module.tsx
constructor(props: any) {
super(props);
DEBUG.construct && debug('Construct AppModule');
this.state = {
navigatorIsVisible: appInitialState.navigator.isVisible,
searchOverlayIsVisible: appInitialState.search.isVisible
} as State;
getAppSettings();
}
search.overlay.smart.tsx
searchOverlayIsVisible$().pipe(
takeUntil(this.destroyed$),
skip(1), // Ignore init state
)
.subscribe(searchOverlayIsVisible => {
DEBUG.subscribe && debug('Observe searchOverlayVisiblity$', searchOverlayIsVisible);
this.setState({ searchOverlayIsVisible });
this.state.searchOverlayIsVisible
});
search.overlay.service.tsx
export function toggleSearchOverlay(isVisible?: boolean) {
if (DEBUG.service && DEBUG.verbose) debug('Toggle search overlay', isVisible);
store.dispatch(
searchActions.toggleSearch(isVisible)
);
return searchOverlayIsVisible$();
}
export const searchOverlayIsVisible$ = () => store$.pipe(
map( state => SEARCH_VISIBILITY(state) ),
distinctUntilChanged()
);
Conclusions
Pushing the initial state in the store$ observable is necessary because we need all the state store observables to recieve their first state. Without this initial state distinctUntilChanged() will not be able to run the comparison between previous and current state. If distictUntilChanged is blocking the obsevables then we end up blocking responses from the webapi. This means we see empty pages even if the state store received the first set of data.
Notice that we are using the component constructor to setup the initial state. Thus, we use the first rendering cycle for useful work. The second rendering will be inhibited by using skip(1) in all state store observables.
Even if we setup init state in constructor we still keep the initial state in reducers as well. All the TOGGLE actions need an initial state to start from.
Be aware that, a lot of processes trigger a second rendering: the init, the router, the event handlers, the observables. As long as React is using the virtual dom for change detection to weed out values that do not actually change, there should be no real impact on DOM rendering performance.
This means it is close to impossible to have just one componentDidUpdate call per route change in LessonsPage. This means we still need to filter out duplicate calls to handlRouteParams().

react meteor data container doesn't update child when props change

I have been struggling with this issue for quite some time and have failed to find any answers.
I use react-meteor-data to manage my data with react in my meteor application. It is working fine when dealing with data for mongo but I can't make it reactive with props.
Here in App.js, I call my container which I want to be reactive and rerender when the state of App change.
<MyContainer someState={this.state.MyState} />
In MyContainer.js I have a createContainer from react-meteor-data
export default createContainer(params => {
Meteor.subscribe('someCollection');
return {
someCollection: SomeCollection.find({}).fetch(),
stateFromParent: params.someState
};
}, MyContainer);
This worked fine when rendering the component for the first time, MyContainer correctly get MyState.
The thing is, when the MyState from App change, I can see in Chrome Dev React tool that it is indeed updated for the createContainer( ReactMeteorDataComponent has a prop with the right updated state) but the createContainer function is not run, thus the props do not update for MyContainer.
So the props are updated from ReactMeteorDataComponent but not for MyContainer who keeps indefinitely the data. It's like createContainer doesn't consider the update of its prop has a change and thus doesn't run its function.
I really think I'm missing something since that seems pretty basic stuff, thank you for your help.
The OP did not mention how the state was changed, so the original example is incomplete. Therefore, I will try to explain the gist of how the container creation works, in hope that understanding it will be useful.
How does it work?
It uses meteor's Tracker to auto-update the wrapped component when its computation is invalidated (i.e, when one of the reactive data sources, such as reactive variables, subscription handles or fetched MiniMongo cursors, has a new value). To learn more about Tracker, consult the Tracker manual. This is an in-depth resource, and is not necessary to understand how the basics work.
It does so in a way that is different from the way you normally approach reactivity tracking in Meteor, since it also needs to re-run the computation whenever the container's props are changed.
The source code is not very long or complex and can be found on GitHub (currently here).
Tracker.autorun((c) => {
if (c.firstRun) {
//...
data = component.getMeteorData();
} else {
// Stop this computation instead of using the re-run.
// We use a brand-new autorun for each call to getMeteorData
// to capture dependencies on any reactive data sources that
// are accessed. The reason we can't use a single autorun
// for the lifetime of the component is that Tracker only
// re-runs autoruns at flush time, while we need to be able to
// re-call getMeteorData synchronously whenever we want, e.g.
// from componentWillUpdate.
c.stop();
// Calling forceUpdate() triggers componentWillUpdate which
// recalculates getMeteorData() and re-renders the component.
component.forceUpdate();
}
})
Whenever the computation is invalidated (and therefore rerun), it stops the computation and forces a re-render of the container, which will re-create a new computation and have the updated data.
The high-level container functionality is here (some parts were removed for brevity):
export const ReactMeteorData = {
componentWillMount() {
this.data = {};
this._meteorDataManager = new MeteorDataManager(this); // (1)
const newData = this._meteorDataManager.calculateData(); // (2)
this._meteorDataManager.updateData(newData); // (3)
},
componentWillUpdate(nextProps, nextState) {
// backup current state and props, assign next ones to components
let newData = this._meteorDataManager.calculateData(); // (2)
this._meteorDataManager.updateData(newData); // (3)
// restore backed up data
},
componentWillUnmount() {
this._meteorDataManager.dispose(); // (4)
},
};
The main points are:
- Before being mounted, a new data manager is created (1). It is in charge of running the computation and populating this.data according to data changes.
- At first and whenever the component should update, the computation is run (2) and the data is updated (3). The update happens whenever the component receives new state or props (in this type of container, it should only be props), and, as we saw earlier, also when the Tracker computation is invalidated, due to the call to component.forceUpdate().
The wrapped component receives the parent's props, as well as the Tracker computation's data as props:
return <WrappedComponent {...this.props} {...this.data} />;
Any more points as to how it should be used?
The react-meteor-data has a short section in the meteor guide.
Generally, the simple example in the guide (as well as the OP's example) should work just fine, as long as the state is set appropriately, using setState() (see the "how does it work?" section above).
Also, there is no need to re-map the container state to props sent to the child, as they are passed along (unless there is a very good reason for doing so).
Do consider the point in the preventing re-renders section if you encounter any performance issues.
From the guide:
export default ListPageContainer = withTracker(({ id }) => {
const handle = Meteor.subscribe('todos.inList', id);
const loading = !handle.ready();
const list = Lists.findOne(id);
const listExists = !loading && !!list;
return {
loading,
list,
listExists,
todos: listExists ? list.todos().fetch() : [],
};
})(ListPage);
in this example, note that the container expects an id prop, and it will also be made available to the wrapped component, as well as loading, list, etc (which come from the container's computation in the example).

Redux DevTools Time Travel fails to update UI correctly (only when reverting actions)

I am in the early development stage of a React+Redux game and have followed Redux best practices: pure reducer, presentational/container component separation, using getState() only in Reducer (as opposed to in action creator) etc. The app seems to be working as expected but when I try to reverse an action using Time Travel, even though the state property map[][] and it's computed connected component prop change as expected, the result doesn't get reflected on the UI properly (specifically the player position on the map doesn't follow what state dictates). When I inspect the state changes I can see that all necessary changes are correctly taking place between different states. Here is my reducer:
const gridReducer = (state, action) => {
if (typeof state === 'undefined'){
let dungeon = new Dungeon();
dungeon.generate();
return {
boardWidth: Math.floor(((70/100) * window.innerWidth) / 20),
boardHeight: Math.floor(((70/100) * window.innerHeight) / 20),
map: dungeon.map,
position: dungeon.playerPosition
}
}
switch (action.type) {
case 'GRID_RESIZE':
return {...state,
boardWidth: action.newBoardWidth,
boardHeight: action.newBoardHeight
}
//This is where I have the issue, map correctly changes both when interacting with the game and when reversing using time travel however the UI fails to update (only in reverse)!
case 'MOVE':
let dungeonObj = new Dungeon(state.map.slice(), {...state.position});
if (dungeonObj.movePlayer(action.direction)) {
return {...state,
position: dungeonObj.playerPosition,
map: dungeonObj.map
}
} else return state;
default:
return state;
}
}
Here is the complete code if you want to take a look! The app currently only supports moving the player in the dungeon by pressing arrow keys and the view is supposed to always be centeral based on the position of the player (player fails to move back when using time travel)
http://s.codepen.io/sabahang/debug/GjrPNQ
PS: Dungeon.generate does use Math.Random but I'm only using this function in initialState and for dispatched actions I'm only making a shallow copy of the generated map by sending the current state to Dungeon constructor and use its other methods (eg. movePlayer)
Found the culprit. It's not Redux's fault at all, it's about the way React works! If you are new to React and you haven't fallen into this trap yet wait for it!
It has to do with the fact that most of the conventional ways of copying a deeply nested object which is needed in Redux to implement a pure Reducer is in fact making a shallow copy of the objects and properties' memory references are still pointing to the original State. React updates the UI based on a deep comparison of the old state and the new one and when some of the references are the same it fails to update the UI properly. Here I have a 2 dimensional array map[][] which is an object and although I'm using ES6 spread operator to avoid modifying the original state because a shadow copy is being made, deeply nested indexes of the original map[][] are being modified. One solution would be to use `Array.map()' to create a completely new object but I ended up using immutablejs and it fixed my problem with the time travel slider.
This is a highly recommended reference if you don't want to spend weeks chasing similar bugs in complicated apps: http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html
and there are tons of immutability helpers to help based on your specific need:
https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities
This one also looks interesting for Redux only:
https://github.com/indexiatech/redux-immutablejs
This question is potentially a duplicate of the following:
React-redux store updates but React does not

Resources