What is "lifecycle features" in React hooks? - react-hooks

While learning hooks came across the definition
Hooks are functions that let you “hook into” React state and lifecycle
features from function components
https://reactjs.org/docs/hooks-overview.html
but nowhere it's mentioned what is lifecycle features ?
Couldn't find exact answer to this query googling as well

Lifecycle in React is talking about a component getting created, updated, and destroyed, aka its "lifecycle". Before hooks were a thing and components were primarily class-based (eg extends React.Component), components had lifecycle methods like componentDidMount or componentDidUpdate. When a component mounted to the DOM, componentDidMount would fire, when the component re-rendered due to a state or prop change, componentDidUpdate would fire, etc. As such, state is also closely tied to this discussion, and previously setState() is how a component would update its state and cause a component re-render.
Functional components have since become the more modern option for creating components, but since functions can't hold their own properties like a class can, hooks were introduced, which allow a function component to reference specific state with useState() or "hook" into the component lifecycle with useEffect().
You can read more about lifecycle on React's State and Lifecycle page: https://reactjs.org/docs/state-and-lifecycle.html
You can also check out how hooks are able to replace a class component's state and lifecycle methods by looking at the useEffect page and useState page in the React docs which show implementations of the same component in both a class-based and function-based component: https://reactjs.org/docs/hooks-effect.html and https://reactjs.org/docs/hooks-state.html

Related

how to make a React component intelligently dispatch a Redux action on user interaction, without passing props that don't affect visual appearance

TL/DR: React components have two kinds of code:
rendering code that draws the component, which depends on certain props that affect the component's visual appearance (call them "visual props"), and
event-handling code, e.g., onclick handlers, which depends on certain props that don't affect the component's visual appearance (call them "event props").
When event props change, they cause the component to re-render, even though its appearance doesn't change. The only thing changing is its future event-handling behavior.
What's best practice for removing event props to avoid unnecessary re-renders, while still allowing intelligent event handling?
Longer version
My question is subtly different from this question about how to give handlers to dumb React components; see below for explanation.
I have an application with many React components (hundreds to thousands of SVG elements; it's a CAD application).
There are many "edit modes" in this application (imagine a drawing program like Inkscape): depending on the edit mode, you might want a left-click to select an object, or drag to draw a selection outline rectangle, or do any number of different edits to the component that was clicked, depending on the edit mode.
In my original architecture, every one of these components had the current edit mode as a prop. Each component would use the mode prop to decide what to do in response to events such as clicks: different sorts of Redux actions are dispatched in response to clicks depending on the current mode. This means that every time the user switches the edit mode, every component gets re-rendered, even though none of them change visually. In a large design, it takes several seconds to re-render.
I've altered it to improve performance. Now, each component is dumber: none of them know the edit mode. But this means they don't know what to do in response to a click. In some cases, I solved this by having each dispatch a "dumber" action that says essentially "I was clicked". Middleware intercepts this action, looks up the edit mode in the Redux store, and dispatches an appropriate smart action based on the edit mode. In other cases, I simply let the component dispatch the original action (e.g., Select), even if that action may not be valid for the current edit mode, and similarly rely on the middleware to intercept and stop the action if it is invalid for the current edit mode.
This solution feels inelegant. Now, many more actions get dispatched, even though most of them are thrown away. It's also nothing like what I find in introductions/tutorials to middleware, which mostly talk about how it's good for async stuff (I don't need any of this to be asynchronous since these actions generally are not talking to the network or files) and side-effects such as logging (no side-effects here; I simply want a user interaction to trigger a normal Redux action to be dispatched).
I feel as though a better solution would be to access the Redux store as a global variable within event handling code. I know this is emphatically not safe to do with rendering code, since it breaks the rule "React views should be a deterministic function of their props and state". But it feels safer to do with event-handling code.
I realize it's common with "very dumb" React components to pass click handlers in as a prop (e.g., this stackoverflow answer), but I don't see this as a solution. If handler has the edit mode encoded in it as a bound value, then the handler itself needs to change when the edit mode changes, which, since the handler is a prop, requires re-rendering the component. So I think this issue I'm describing is orthogonal to whether the handler is passed into the component as a prop, or written specifically for the component.
So to summarize, there's three options I see:
Pass all data required for intelligent event handling as props. (causes unnecessary re-renders)
Have React components dispatch actions "promiscuously", and rely on middleware (which has access to the Redux store) to stop and/or transform the action if necessary. (As I implemented it, is harder to understand, and puts lots of unrelated application logic in one place, where it feels like it doesn't belong. Also makes for a messier Redux history of actions, making it harder to debug using Redux DevTools, and is not a pattern I've seen in any documentation/tutorial on Redux middleware.)
Allow event handler code (unlike rendering code) to access the Redux store as a global variable, to make intelligent decisions about what action to dispatch. (Seems okay, but scares me to use global variables in this way, and I'm worried that it could cause a problem I'm not seeing.)
Is there a fourth option I'm missing?
I have an idea for how to solve this in a way that feels close to the Redux spirit. (Though I still lean towards accessing global variables in event handlers to solve the problem.)
Redux has some notion of "action creators", which is a function that returns an action object. This always seemed like an unnecessary layer of abstraction to me. But perhaps a similar idea can be used here. (I use Dart, not Javascript, so the code below is Dart; hopefully the answer makes sense.)
The idea is to have a new type of action in called ActionCreator<A extends Action> (subtype of Action). An ActionCreator<A> is an object with a method of type
A create(AppState state)
In other words, it takes the whole AppState and returns an Action. This lets it do the necessary data lookups. As an object, it can contain fields that describe data gathered from the code (usually View event handler code) that instantiated it. For example, it could reference a Selectable to select. create() returns either null or some special value to indicate that the action should be thrown away.
For example, if we have a click handler, we'd dispatch an ActionCreator
class Select {
final Item item_clicked;
Select(this.item_clicked);
}
class ClickedAction implements ActionCreator<Select> {
final Item item_clicked;
ClickedAction(this.item_clicked);
Select create(AppState state) =>
state.ui_state.select_mode_is_on ? Select(this.item_clicked) : null;
}
// ...
onClick = (event) {
props.dispatch(ClickedAction(props.item));
}
And in middleware, once we have access to the full state, this can be turned into a concrete action, but only if it's legal. But the nice thing is that the next piece of code is generic and handles any such ActionCreator, so I wouldn't have to remember to keep editing this code whenever I create a new Action that needs to be "conditionally dispatched".
action_creator_middleware(Store<AppState> store, action, NextDispatcher next) {
if (action is ActionCreator) {
var maybe_action = action.create(store.state);
if (maybe_action != null) {
dispatch(maybe_action);
}
} else {
next(action);
}
}
The disadvantage of this is that it's still dispatching many more actions than we really need; most will get thrown away. It's a "cleaner" implementation of what I need, but I still think that for asynchronous event handlers, access the Redux store as a global variable is probably perfectly fine. I don't see in that any of the problems one would expect if the view code went outside of its React props and accessed global variables.

Need an Reactjs indeterminate progress bar and need advice on that

I learn React and Redux and in React I start an async fetch in one Component and the result from that is showen in another Component that uses the Redux mapStateToProps for that Store state
I come from Java and want to detach this by maybe creating a new React Component that will listen for start/stop command to toggle the indeterminate progress.
I read React docs and code but has not really written any code jet.
Is this a good approach? If so could/should I use the Redux mapStateToProps on this Component to let it know what state the progress bar should be in?
Is there some other way to do this maybe not using Redux Store since the state of the progress bar is not something that should be in the Redux store!?

Reliable way to access data in Polymer (inside dom-repeat / event handler)

I need to capture data from an instance generated by <template is="dom-repeat"> in Polymer (v1.2.4) and I am not sure what would be the safest way to do so considering the myriad of Shadow DOMs available (client browser might be polyfilled etc).
A simple example:
<template is="dom-repeat" items="[[myItems]]" id="collection">
<paper-card on-tap="handleTap">
(...)
What is the most reliable way to access the model data from the event handler?
1.
handleTap: function(e) {
var data = e.model.get('item.myData');
}
2.
handleTap: function(e) {
var data = this.$.collection
.modelForElement(Polymer.dom(e).localTarget)
.get('item.myData');
}
My concern is that the simplest (#1) option might be working as expected in my environment but can get buggy in other browsers.
And even in option #2, I am not confident if it is really necessary to normalize the event target (as recommended in the official Polymer guide on events) prior to passing it to modelForElement.
Both should work; but, it seems you should fire a custom event though over trying to inspect a child model. What ever component that has "item.myData" should fire a custom event on tap with "item.myData" as part of the event. Then you should setup a listener for that custom event.
See custom events for more details.

Deactivate composed viewmodel

There is a lot of questions about this but I cannot find any answer that works with the latest durandal version (2.1.0).
I am showing a child viewmodel inside my page using this:
<div data-bind="compose: { model: activeScreen, activationData: {id:selectedId}}"></div>
activeScreen is an observable to which I pass an string like this: viewmodels/child
It works, and the child viewmodel gets activated and shown on the screen. But when I change activeScreen, I need the child viewmodel to run deactivate. Is it possible? How?
So long as your child views are instance modules and not singleton modules, you can move that code to the detached handler. That's what we do as well: All of our child views are managed through dynamic composition, not child routing (which just doesn't work for the enterprise-style app). The deactivate handler comes into play in the context of routing.
By dynamic composition I'm referring to the swapping in and out of child views/viewModels through an observable.
But, again, the key to making this work is that the child views must be instance modules. That way they'll actually get unloaded from memory. If you go with singletons, the modules will never get detached (although there are ways to force this to happen).

Is Ext JS's MVC an anti-pattern?

I work in a team of 25 developers. We use ExtJS MVC pattern of Sencha. But we believe that their definition of MVC is misleading. Maybe we might call their MVC an anti-pattern too.
AMAIK, in MVC controller only knows the name or the path of the view, and has no knowledge on the view's internal structure. For example, it's not the responsibility of the controller, whether view renders the list of customers a simple drop down, or an auto-complete.
However, in Ext JS's MVC, controller should know the rendering of view's elements, because controller hooks into those elements, and listens to their events. This means that if an element of the view change (for example a button become a link), then the relevant selector in the controller should change too. In other words, controller is tightly-coupled to the internal structure of the view.
Is this reason acceptable to denounce Ext JS's MVC as anti-pattern? Are we right that controllers are coupled to views?
UPDATE (March 2015): Ext 5.0 introduced ViewControllers that should address most of the concerns discussed in this thread. Advantages:
Better/enforced scope around component references inside the ViewController
Easier to encapsulate view-specific logic separately from application flow-control logic
ViewController lifecycle managed by the framework along with the view it's associated with
Ext 5 still offers the existing Ext.app.Controller class, to keep things backwards-compatible, and to give more flexibility for how to structure your application.
Original answer:
in Ext JS's MVC, controller should know the rendering of view's elements, because controller hooks into those elements, and listens to their events. This means that if an element of the view change (for example a button become a link), then the relevant selector in the controller should change too. In other words, controller is tightly-coupled to the internal structure of the view.
I actually agree that in most cases this is not the best choice for the exact reasons you cite, and it's unfortunate that most of the examples that ship with Ext and Touch demonstrate refs and control functions that are often defined using selectors that violate view encapsulation. However, this is not a requirement of MVC -- it's just how the examples have been implemented, and it's easy to avoid.
BTW, I think it definitely can make sense to differentiate controller styles between true application controllers (control app flow and shared business logic, should be totally uncoupled from views -- these are what you're referring to), and view controllers (control/event logic specific to a view, tightly-coupled by design). Example of the latter would be logic to coordinate between widgets within a view, totally internally to that view. This is a common use case, and coupling a view-controller to its view is not an issue -- it's simply a code management strategy to keep the view class as dumb as possible.
The problem is that in most documented examples, every controller simply references whatever it wants to, and that's not a great pattern. However, this is NOT a requirement of Ext's MVC implementation -- it is simply a (lazy?) convention used in their examples. It's quite simple (and I would argue advisable) to instead have your view classes define their own custom getters and events for anything that should be exposed to application controllers. The refs config is just a shorthand -- you can always call something like myView.getSomeReference() yourself, and allow the view to dictate what gets returned. Instead of this.control('some > view > widget') just define a custom event on the view and do this.control('myevent') when that widget does something the controller needs to know about. Easy as that.
The drawback is that this approach requires a little more code, and for simple cases (like examples) it can be overkill. But I agree that for real applications, or any shared development, it's a much better approach.
So yes, binding app-level controllers to internal view controls is, in itself, an anti-pattern. But Ext's MVC does not require it, and it's very simple to avoid doing it yourself.
I use ExtJS 4's MVC everyday. Rather than spaghetti code, I have an elegant MVC app that has tightly defined separation of concens and is ridiculously simple to maintain and extend. Maybe your implementation needs to be tweaked a bit to take full advantage of what the MVC approach offers.
Of course the controllers are bound to the views in some way. You need to target exactly which elements in your views you want to listen to.
eg: listen to that button clicks or to that form element change or to that custom component/event.
The goal of MVC is components decoupling and reusability and the Sencha MVC is awesome for that. As #bmoeskau says, you have to be careful in separation of the view controllers (builtin for the view/widgets itself) and the application controllers (top level views manipulations) to take full advantage of the MVC pattern. And this is something not obvious when your read http://docs.sencha.com/ext-js/4-1/#!/guide/application_architecture. Refactor your MVC approach, create different controllers, create custom component, and embrace the full ExtJS MVC architecture to take advantage of it.
There's still a slight problem in Sencha approach IMHO, the MVC refs system doesnt really work when you have multiple instances of the same views in an Application. eg: if you have a TabPanel with multiple instances of the same Component, the refs system is broken as it will always target the first element found in the DOM... There are workarounds and a project trying to fix that but i hope this will be adressed soon.
I'm currently undergoing the Fast Track to ExtJS 4 from Sencha Training. I have a strong background in ExtJS (since ExtJS 2.0) and was very curious to see how the MVC was implemented in ExtJS 4.
Now, previously, the way I would simulate kind of a Controller, would be to delegate that responsibility to the Main Container. Imagine the following example in ExtJS 3:
Ext.ns('Test');
Test.MainPanel = Ext.extend(Ext.Container, {
initComponent : function() {
this.panel1 = new Test.Panel1({
listeners: {
firstButtonPressed: function(){
this.panel2.addSomething();
},
scope: this
}
});
this.panel2 = new Test.Panel2();
this.items = [this.panel1,this.panel2];
Test.MainPanel.superclass.initComponent.call(this);
}
});
Test.Panel1 = Ext.extend(Ext.Panel, {
initComponent : function() {
this.addEvents('firstButtonPressed');
this.tbar = new Ext.Toolbar({
items: [{
text: 'First Button',
handler: function(){
this.fireEvent('firstButtonPressed');
}
}]
});
Text.Panel1.superclass.initComponent.call(this);
}
});
Test.Panel2 = Ext.extend(Ext.Panel, {
initComponent : function() {
this.items = [new Ext.form.Label('test Label')]
Test.Panel2.superclass.initComponent.call(this);
},
addSomething: function(){
alert('add something reached')
}
});
As you can see, my MainPanel is (besides the fact that is holding both panels) also delegating events and thus creating a communication between the two components, so simulating sort of Controller.
In ExtJS 4 there is MVC directly implemented in it. What really striked me was that the way the Controller actually fetches the components is through QuerySelector which in my opinion is very prone to error. Let's see:
Ext.define('MyApp.controller.Earmarks', {
extend:'Ext.app.Controller',
views:['earmark.Chart'],
init:function () {
this.control({
'earmarkchart > toolbar > button':{
click:this.onChartSelect
},
'earmarkchart tool[type=gear]':{
click:this.selectChart
}
});
}
});
So as we can see here, the way the Controller is aware of the earmarkchart button and tool is through selectors. Let's imagine now that I am changing the layout in my earmarkchart and I actually move the button outside of the toolbar. All of a sudden my application is broken, because I always need to be aware that changing the layout might have impact on the Controller associated with it.
One might say that I can then use itemId instead, but again I need to be aware if I delete a component I will need to scatter to find if there is any hidden reference in my Controllers for that itemId, and also the fact that I cannot have the same itemId per parent Component, so if I have an itemId called 'testId' in a Panel1 and the same in a Grid1 then I would still need to select if I want the itemId from Panel1 or from the Grid1.
I understand that the Query is very powerful because it gives you a lot of flexibility, but that flexibility comes at a very high price in my opinion, and if I have a team of 5 people developing User Interfaces and I need to explain this concepts I will put my hands on the fire that they will make tons of mistakes because of the points I referenced before.
What's your overall opinion on this? Would it be easier to just somehow communicate with events? Meaning if my Controller is actually aware of what views he's expecting events, then one could just fire an event dosomethingController and the associated Controller would get it, instead of all this Query problem.
I think if you use the Sencha Architect to produce the Views then Inherit from that View to create Your own View.
Now this View Can be responsible to hook up to any events and raise meaningful events.
This is just a thought...
//Designer Generated
Ext.define('MyApp.view.MainView', {
extend: 'Ext.grid.GridPanel',
alias: 'widget.mainview',
initComponent: function() {
}
});
//Your View Decorator
Ext.define('MyApp.view.MainView', {
extend: 'MyApp.view.MainViewEx',
alias: 'widget.mainviewex',
initComponent: function() {
this.mon(this, 'rowselect', function(){
this.fireEvent('userselected', arguments);
}, this);
}
});
I think there is a pretty bad problem here - its very difficult to shard isolated units within a page.
The approach I'm experimenting with (which makes it somewhat easier to write tests aswell) is to have a vanilla js context object for each page which contains the logic (and has the benefit of being very easy to break up and delegate to different objects). The controllers then basically call methods on the context objects when they receive events and have methods on them for retrieving bits of the view or making changes to the view.
I'm from a wpf background and like to think of the controllers as code-behind files. The dialog between presenter/context and view is a lot chattier than wpf (since you dont have binding + data templating) but its not too bad.
Theres also a further problem I haven't had to solve yet - the use of singletons for controllers causes problems for reuse of UI elements on the same page. That seems like a serious design flaw. The common solution I've seen is (yet again) to pile everything into one file and in this case to ditch the controller altogether. Clearly that's not a good approach as soon as things start to get complicated.
It seems like getting all the state out of the controller should help though and you'd then have a second level of context objects - the top level one would basically just assign a unique id to each view and have a map of context=>view and provide dispatch to the individual context methods - it'd basically be a facade. Then the state for each view would be dealt with in the objects dispatched to. A good example of why statics are evil!

Resources