Sending "commands" into React components? - reactjs

Just to provide some context and hopefully make my question clearer, I'll present a scenario that works, and will make sense.
Let's say we have an AudioTrack component, which renders play, stop, and seek controls. When the user seeks, that info is passed back to us:
<AudioTrack playingId={this.state.playingId} onSeek={this.handleSeek} />
(playingId indicates the ID of the currently playing track)
But what if we move the seek control outside the audio tracks, and so the seek control exists in the parent <TrackList> and will control whichever track is currently playing. Now I need to "send a command" with data into the appropriate track, e.g.:
<AudioTrack playingId={this.state.playingId} seekTo={this.state.newPos} onSeek={this.seekComplete} />
This feels hacky to me, but basically when the user lets go of the seek bar, it would set the newPos state in TrackList, which triggers AudioTrack's componentWillReceiveProps. It seeks to the new position, then could report back via onSeek which would do this.setState({ newPos: null }) in TrackList.
Am I thinking about this correctly, or is there a better way? In an ideal world, I could just directly call a method of the AudioTrack.

You must use redux or flux for these kind of things. When working with react, you should store EVERY component in a redux/flux store. In fact you should not use constructor(props) ever, and store everything in the store. This will fix your 'hacky' issue. You can make the direct call to your redux store which is exactly what you are looking for.
I cannot iterate more on the importance of redux and react. You must use them together.
https://github.com/reactjs/redux

Related

Is there a React (state related) event pub sub pattern when replacing Redux?

It seems to me the trend is replacing Redux with React Context API and useReducer.
I personally do not like a huge store at root with states from user data to if a dialog is opened all mixed together.
Switching to React Context API allowed me to push the state down and closer to where they will be used. However I can only push it down to the level that child components are either displaying or modifying that value. For example:
<Parent>
<CounterDisplay/>
<CounterIncreaseButton/>
</Parent>
I have to have count on Parent and create a Context. Within the Context, I'll add the count value and an increaseCount method (or a state and a dispatch function when use reducer pattern). Then I provide the context to those 2 child components. Now one can display it and one can modify it.
Now what if I need another button located far from this part of the component tree that also need to change the count value? I have to lift the state up and maybe all the way to the root. That feels strange to me.
2nd issue is when states are scattered at multiple level along the path in the tree, when something happens say user click a button, you may need to call multiple functions from multiple contexts (or dispatch multiple actions, one for each state that may or may not change). Unlike when use Redux since everything is at the root, you just need to dispatch one action.
So what if instead I have an event pub/sub at the root level? I can have the counter state and code manipulate it pushed down even more to CounterDisplay. CounterDisplay need to subscribe to the pub/sub system and listen to the event and update counter correspondingly. And whichever component want to change the counter can just raise an event.
What am I missing in this pattern? Circular event loop? Raise conditions? Feels a good pub/sub library can prevent these. I looked around and did not find something existing. I looked at RxJS but don't feel that fits.
Thanks in advance for sharing your thoughts.
You've basically just described the exact reason for Redux to exist :)
Redux is "an event pub/sub at the root level", and can specifically be beneficial in cases where widely separated components need to make use of the same data.
You may want to read my post Redux - Not Dead Yet!, which describes how Redux fits into today's ecosystem (including comparisons vs context) and some of the reasons you might find it useful.
If you are put off redux by the amount of boilerplate.
I would suggest taking a look at redux-zero.
Under the hood. react-redux uses a context provider at the root.
When you use the connect Higher-Order-Component that is using the context.
Same with the redux hooks.
So it's quite normal to have the provider at the very top.
It would be the same for any library like the react-router to do the same.
I would suggest you keep trying without redux so you can learn more. Put the provider at the root. It won't impact performance in anyway.
Isolate the context you create and the provider to a singleton file and import it to the components you need

Use cases for when not to use redux?

I was always wondering if I should use redux store all the time even when it's not really necessary.
For example:
I have form with select field that has some options I fetch from API. Let's imagine that form is for adding new car listing and select options are car models. Should I directly call API from component and display options or should I create store CarModels or something like that and store results from API there and then map those values to state in component?
I have "Favorites" feature, when you click heart next to some item (let's say a car), do I need to go through dispatching all events FAVORITE_CAR_REQUEST, FAVORITE_CAR_SUCCESS etc... or is it good enough to just call API directly from component (using some kind of api-service of course).
It's related to question above. If I have screen where I show favorites, I should then probably have store and connect everything with actual favorite action so I update the list. On new favorite, API will return favorited item, should I push that one in list of favorites already in store or should I just load again latest list when user opens up the favorites screen?
Thanks in advance.
I think this is a perfectly valid question. What I feel like you're trying to ask is if you could/should mix react state and the redux store. The answer is sure! Just think about where you need to use that part of state before deciding where to store it. If you need a part of the state in multiple components, it probably makes sense to use Redux. If you only need state locally, perhaps to set form validation errors, maybe use react's state management if you feel like it. React and redux are both meant to be flexible, so as long as you're consistent in when you use the redux store and react state you should be good.
I found this article that also explains this pretty well: https://blog.jakoblind.no/is-using-a-mix-of-redux-state-and-react-local-component-state-ok/
I tend to use redux when the state has to be accessed globally / complex logic that i want to be logged properly

Better approach to create react-redux layout header component

I'm going to create a large scale application with react and redux and start to build the header component of the layout very first time. The layout header will have 4 child parts.
1. Icon that will show all the online users after clicking on it
2. Icon that will show latest 10 notifications after clicking on it
3. Icon that will show latest 10 messages received after clicking on it
4. Logged in user name that will be dropdown button to show some "My profile, logout, account setting" navigation links.
As I've read about redux at so many places that we must have as less as possible stateful components. So by keeping this concept in mind, I thought to have only one header smart container with a header state consisting default states for all icons like
{headerOnlineUsers:[], headerMessages:[], headerNotifications:[]}.
So if I follow the redux best practice and create only one smart header container with 4 different dumb components, the each and every time the state of any of them is changed, the whole header container will get re-rendered.
But I want to re-render only the state changed component.
For example, If I get a user joining the chat through websocket, It will dispatch an action and the reducer will update the headerOnlineUsers list of the state. But In this case I want my only online users component to be re-render not the whole header container.
Can anybody please suggest me the best approach to implement this kind of layout.
I would not go for your option, but would prefer a more granular approach.
What if you split the header in 2 later on, or want to display the button somewhere else ?
What if you have another version of your header for a subsection of the website ? You would need another container that gather again all the info.
The logic in general in redux is to separate what is data-sensitive and what is not. Your individual lists are data sensitive, the header in itself has no reason to be so far. What's more, if you want to attach actions to the icons it will quickly become messy to bring back thoses actions to the header and then its container, and even messier if you ever need to move them.
The way I would do it (and the way I do it right now as I halso develop quite huge app with react and redux), would be to have a store with those 3 lists of items, and then 3 containers, one for each icon/dropdown that links one aprticular list to the component.
Then the header is just a stateless function holding all the containers together. This way if you ever need to move one button somewhere else or reuse it, you just import the container and voila!
React.js will take care of only re-rendering what is required. So this problem is already solved for you :-)
What you plan to do sounds sane to me, I just would change one bit: The smart header container should not use internal state. Instead, your redux store should look like this:
{headerOnlineUsers:[], headerMessages:[], headerNotifications:[]}
and you should pass the redux store into the smart header container (have a look at the connect function in the redux documentation). This way, you can use the redux store contents in your component via this.props, and that's it.

Am I thinking in in react correctly about form behaviour

I'm debating refactoring parts of a site I'm working on from jQuery into react. Before I start I'd appreciate some feedback on my thought process so far.
<DeviceChooser>
<ManufacturerSelect />
<DeviceSelect />
<ButtonOne />
<ButtonTwo />
<DeviceChooser>
That is the desired component. It's behavior is:
The parent (DeviceChooser) gets some json data via ajax on componentWillMount.
It passes part of that data to ManufacturerSelect, which is a select field.
Once something is selected there, DeviceChooser passes some other data to DeviceSelect, which is also a select field.
Once something is selected there, if some conditions are met, both Button1 and Button2 become active
Clicking Button1 or Button2 would redirect the page to a specified url, with parameters set depending on the 2 select fields.
More practically speaking, you choose your phone manufacturer, then in the next select you choose your device from that manufacturer, then you get redirected to either page1 or page2 with some get parameters depending on the button you press.
My questions are:
Who's responsibility should it be to decide whether the buttons should be active? The DeviceChooser or the Buttons?
Who should compose the redirect URL? The Chooser or the Buttons?
I'm going to have variations of this DeviceChooser component all over the website, what can I do to ensure at least some reusability? The caveat being that sometimes it will have more select fields that just 2, and other times different select fields will be part of the equation depending on state (Like, if your device is a laptop, you also specify what shape of edges the device has)
I'm really grateful to any feedback at all. I've also created a Gist with the code I've come up with so far, if it helps.
One of the methodologies that I have followed since getting invested in React is using containers. Containers essentially are components that are responsible for retrieving and manipulating the data and then passing all the relevant data down to all the child "dumb" components that are simply responsible for rendering said data.
Operating under this premise (or something similar) I would suggest doing calculations in the container on the initial data, and pass everything down.
So in this instance we should be do the following in the container (or parent component)
Get JSON data via componentWillMount
Manipulate the data and pass it to ManufacturerSelect
The other questions depend on which framework you're using. Are you able to elaborate on this? Are you using Redux, Flux, ReFlux etc?
I've had a quick look at your code, one super useful thing that I think you should do is specify PropTypes for each component. This helps immensely for debugging, and when you're talking about reusing components in several distinct locations this will be crucial. Also (without understanding the full context) is it necessary to use state everywhere in your components? Would it be possible for them to simply render the props passed down to them? (again this depends a little on the store you'
re using).
It's a relatively generic question, but my line of thinking (quite opinionated) would be as follows:
Your Buttons should become simple, generic Button components that should for this example have the following props (besides perhaps some styling props):
disabled
title
onClick
Your Device Chooser is the one who's got the awareness that components mix together, that these buttons should actually continue to do something after they're clicked, so you'll want to handle that knowledge solely within that component. It glues the rest together (passes data around) and should therefore also make these decisions.
<DeviceChooser>
<ManufacturerSelect data={this.state.manufacturers} />
<DeviceSelect data={this.state.devices} />
<Button
disabled={this.state.selectedManufacturer === null ? true : false}
title='Manufactuer details'
onClick={this.handleManufacturerDetailsClick.bind(this, this.state.selectedManufactuer}}
/>
<Button
disabled={this.state.selectedDevice === null ? true : false}
title='See device details'
onClick={this.handleDeviceDetailClick.bind(this, this.state.selectedDevice }}
/>
<DeviceChooser>
Device chooser's method than can be something like:
handleDeviceDetailClick(device) {
history.pushState('/device/detail/' + device.id);
}
You want to separate your functional components from the stateless ones. A good read for this is http://tylermcginnis.com/functional-components-vs-stateless-functional-components-vs-stateless-components/

Responding to high frequency state changes in React.js / Flux

I have an application which receives messages from a server every 1 second and via socket.io these messages are broadcast to a react component.
My react component is using a flux style architecture, so it calls an action when a message is received which adds (or updates) a record in a collection in a store.
My component monitors changes on the store and then updates the UI when it changes. In this case, it draws a marker on a map.
The problem is that due to the frequency of the updates my component redraws all markers every second, which I don't want.
I'm looking for an approach to allow my map component to respond to changes in a collection in a store, but not have the state update every second.
I thought I could have a collection for the raw data, and then only update the networkGPS collection selectively in my store, but the component seems to change based on any property in the store as they all seem to be part of its state.
In summary I'm looking to:
Collect data every 1 second in raw form and add this data to a store.
Bind a component to a collection in this store which I update only when changes require a UI redraw.
What I think I need to do:
Either:
Avoid putting the raw data into the state of the store (at the moment I'm unsure how to declare a collection which is not part of the state)
OR
Make my component more clever as to when it redraws, so it doesn't try to redraw on each state change.
I'm not sure which is the most appropriate solution for the problem, any info would be gratefully receieved.
Sorry if the question is a little unclear, I'm new to react / flux. Please do ask for any clarifications.
EDIT
At the moment I am getting round this issue by storing an array of the items (map markers in this example) I want my component to display in the component itself :(
When the state changes and render is called, I intercept the state properties, decide if anything needs to change in the collection of markers and only amend them if needed. If no changes are needed, the collection in the component stays the same and no redraw is performed.
This is not ideal, I feel like I should be able to allow my component to respond to only specific state changes but I've not found it yet, still looking for a better answer.
There are a couple of ways to approach this:
1) Use shouldComponentUpdate to compare old props and new props, re-rendering only the new marker states.
2) Switch to a cursor-based state tree that automatically triggers renders only for views that are bound to the specific parts of the state tree that have changed. I believe Baobab is one such library. See also Om (written in ClojureScript) and probably a number of other Flux implementations.
Given the impact of #2 my vote would be to go the route of #1.
I am not quite sure what's the problem that you face with updates every second.
However, the one place where you can queue the updates is between the socket.io and calling the action.
Instead of calling the action every second, you could queue the updates before sending it across to react.

Resources