I'm wondering if it is bad practice to have 'fat' gDSFP functions. Currently, I have a component that takes in some data and does a bunch of data manipulation
function getDrivedStateFromProps(nextProps, prevState) {
// start doing data manipulation
and along the way if it encounters an error condition it returns a new error slice of state
const someValue = nextProps.something * myFunc()
if (someValue === badThing()) {
return {error: true};
}
// continue doing data manipulation
This repeats several times before it finishes all data manipulation and returns the derivedState that my component needs. I'm curious on what the communities views are on a 'fat' gDSFP functions. My component only runs gDSFPs when the external data source has changed and it needs to derive new state so I don't see where else this logic could live.
I think you may actually be in a situation where using getDerivedStateFromProps might not be the best approach at all. In your question you state...
My component only runs gDSFPs when the external data source has changed and it needs to derive new state so I don't see where else this logic could live.
Given this statement, it sounds like you can use memoization. Here is this concept discussed on the react docs.
The basic idea is that managing getDerivedStateFromProps can get hairy especially when you have lots of logic going on there. If the reason why you want to capture your props in state is only to get a performance boost, then memoization might be your friend.
The idea here is that you do not want some logic to run every time your props change, so what this will buy you is that if you have a function whose arguments are the same as they were before, as an example state has not changed, your function will return the last computed result which it had stored in the cache. This library handles this beautifully and is pretty easy to use.
Another concern people often have which may prompt them to reach for getDerivedStateFromProps is to ensure that their component does not render unless if the props have in fact changed, so by having the component render based off of computed state this can be achieved.
But this too can be achieved without using getDerivedStateFromProps. Using PureComponent can give you this same result with much less fuss. Another option can be shouldComponentUpdate.
In short, unless you have some very specific reason that getDerivedStateFromProps is right for your use case, you may be better off reaching for a solution that involves less hair pulling.
Related
Can you globally instantiate a class and that will be reliable on react-native? i.e.
// logs.ts
const instance = new Instance()
export { instance }
// something.tsx
const Component = () => {
instance.method()
...
}
If method were to increment a property by 1 would that property always have been incremented the number of times method was called throughout the project?
The primary benefit here is simplicity. For example, it's easier to define
class SomeClass {
constructor(properties){}
addProperty(key,value){ this.properties[key] = value }
}
than it is to do the equivalent in redux. Why don't people do the above more often?
Just changing the value on some object will not result in state changes/cause a component to re-render. You still need a state provider to have the state set to. If you want to change the state of that Provider you'll need to run setState (or dispatch and useReducer) which means you'll need to pass the dispatch of that function around to all of its children. As your app grows larger you'll definitely want to/need to useReducer and perhaps even several providers, you'll be re-implementing redux which is only about 200 lines of code anyway. So the question will become why did you re-implement redux which is such a popular library that most people know exactly how to use and there is plenty of documentation for, in favor of your homegrown version of redux which doesn't provide much additional value?
In redux a primary benefit is the tooling like redux-logger, redux-thunk, redux dev tools and time travel and others etc which allows you to replay the changes made or undo them. Sure it is easy to modify an object but using redux allows you to testably and consistently see how an object (the redux state) changes over time and undo them. You can test that action creators return the correct actions. You can separately test that given specific actions the reducer behaves as expected and replay that with mockStore. In short, using redux you get the enterprise version supported by a large community of experts who have helped improve and implement essentially the simple class that you demoed.
Although you can do this, it breaks away from the point of redux. Redux is meant to be a centralized state management store, and therefore, allow components to
access shared state from a single location and analyze shared states, because it comes from one place.
track history and actions taken to get state there
makes maintaining, debugging and collaborating on a codebase
much easier.
Doing the first option, all these benefits are lost.
That said, if you have multiple components in one file, and you want them all to share that same global state (not exporting it), using the class constructor to do so isn't a big deal, as long as the project isn't being worked on by other developers.
However, it would make more sense in that case to make an overall component class, and pass state down, as that is the point of React. Declaring a class outside and trying to manage state in the first way, would be a declarative approach, which is what React doesn't want developers to do (i.e there is a better way, like creating a hierarchy of components). That way, components re-render on a change in value, and no weird bugs arise
This has come about because during prototyping, I'm finding that incoming new prop(s) might be an array of complex objects, so prevProps.myProp===this.props.myProp is always false. JSON.stringifying them both before comparison works, but feels unreliable.
I'm trying to write a reusable function for Class Components that compares prev/new props in react's componentDidUpdate. For example:
componentDidUpdate(prevProps) {
// First, get difference here, possibly using a lib like npm 'deep-object-diff'.
// ...or just JSON.stringify both and do string comparison
const changes = getTheDifference(prevProps, this.props)
// Don't do anything if there's no changes.
if (!changes) return
// Then I want to do something like:
this.setState( Object.assign(this.state, changes) )
}
...that would mean any time the incoming props change, those get immediately reflected in state. I'm having some issues finding a suitable diff lib, but I still feel like I shouldn't be having to do this and am missing something - is there a generally accepted "normal" way to do this, or is me having this problem just a sign of:
The component is trying to do too much/state too complex?
I should be mapping props to state with something like Redux? (trying to avoid this until absolutely necessary)
I'm overthinking it, sometimes the logic in a component's componentDidUpdate is just complex and unique?
that would mean any time the incoming props change, those get immediately reflected in state
Yes, that's wrong. When I first started out I was doing the same thing.
state is something "owned" by that component (noteworthy: not all components need state at all!).
props are something that are "owned" by a higher component.
Best example:
ComponentA passed an id to ComponentB as a prop. ComponentB uses that id to make an API request (for example).
The status/results of that API request form part of ComponentB's state (it's "owned" by your component). The id passed in as a prop is NOT part of ComponentB's state (it's owned by ComponentA, or maybe somewhere higher in the tree that passed it as a prop to ComponentA).
When the id prop changes, ComponentB will need to make another API request.
EDIT:
The complexity of lifecycle methods is why React has highly encouraged functional components.
You should think of components as functions. It's a lot easier to write the logic if you just think of it as input -> output.
componentDidUpdate has been moved to useEffect with a dependency list so you can say - run this function, but only when this prop changes - which is a nice way to break down massive componentDidUpdate methods into tiny readbale/testable chunks.
I've heard a lot of people say hooks ruined react, but I highly disagree with them. React saw the problems people were running into in the community by misusing class/instance based components and instead of lecturing about how to use them correctly, they nudged people to writing better components by introucing a simpler API - functions, while they still can and will be abused - are generally simpler to deal with than classes and align nicely with composition over inheritance and declarative over imperative.
I use Aurelia on a daily basis. Recently, I have been looking into using Redux (i.e. I built a couple of small trial apps using Aurelia+Redux) and have been really impressed (my development workflow and clarity of reasoning about my application is greatly improved). I have decided that I would like to start working it in to real-world applications.
With that said, I have a concern regarding performance (I have looked around at posts about performance but haven't seen my issue address directly). I think this question is not specific to Aurelia, more a question about Redux and using it with non-react libraries.
Let me preface my question with my understanding of Redux (perhaps my question is really arising from a flawed understanding?). Essentially, the way I understand Redux is that there is a store (a javascript object) and a reducing function. Now, the reducing function may be defined as a tree of functions (each responsible for modifying a specific branch of the overall store), however, in reality, Redux receives a single reducing function (it has no way of knowing how many functions were composed to create this single function).
The way I am using Redux is like so (just an example):
#inject(Store)
export class TodosListCustomElement {
constructor(store) {
this.store = store;
}
activate() {
this.update();
this.unsubcribe = this.store.subscribe(this.update.bind(this));
}
deactivate() {
this.unsubcribe();
}
update() {
const newState = this.store.getState();
this.todos = newState.todos;
}
toggleCompleted(index) {
this.store.dispatch({
type: UPDATE_TODO,
payload: {
index,
values: {
isCompleted: !this.todos[index].isCompleted
}
}
});
}
}
Essentially, each component down the component tree, subscribes itself to store changes and refreshes the data it needs from the store.
My concern is that there seems to be a lot happening on each published action. For example, say I have a large application with a similarly large store and reducer tree. Suppose there is some throttled textbox that dispatches changes to a single text field (in one item of a list) in the store every 250 ms. That would mean that as a user types, the entire reducer function is executed every 250ms (which could mean executing quite a large number of its descendant reducers) as well as all the subscribing functions are executed as well. Basically, it seems like there is a lot of overhead to change even the smallest part of the store.
Contrast this with a standard binding (in Aurelia) where there is just a single bound function (mutation observer) that needs to execute every 250ms to update the model...
Since I am new to Redux, I guess there is a good chance that I am naively misunderstanding something etc. I apologize in advance and hope to be corrected/put on the right track (because my limited experience using Redux has been very enjoyable).
Thanks in advance
You're actually describing the situation pretty well, on multiple levels.
First, the React-Redux bindings do a lot of work to ensure that connected components only actually re-render when some of the data relevant to a given component has changed. This is done by having a connected component supply a function called mapStateToProps, which extracts the data that component wants from the store state. The wrapper components generated by connect will re-run their mapState functions after each dispatch, and do shallow comparisons between the latest returned values and the previous returned values to see if the data has changed. That cuts down on the amount of actual UI updates that need to be done.
There's also tradeoffs involved in how you handle connected forms. Yes, dispatching an action for every single keystroke is likely to be inefficient overall. I personally use a React form wrapper component that buffers those text input changes locally, and only dispatches a debounced Redux action after the user is done typing.
The React-Redux bindings were recently rewritten, and are now primarily based on memoized selector functions rather than having most of the logic inside of React components. I don't know how the Aurelia bindings are put together, but I suspect that they could probably leverage a lot of the work that's been done to optimize the React bindings.
You may be interested in some of the articles I have on Redux-related performance. See the Redux FAQ question at http://redux.js.org/docs/faq/Performance.html#performance-scaling , as well as the articles in my React/Redux links list at https://github.com/markerikson/react-redux-links/blob/master/react-performance.md#redux-performance .
So I started learning React a week ago and I inevitably got to the problem of state and how components are supposed to communicate with the rest of the app. I searched around and Redux seems to be the flavor of the month. I read through all the documentation and I think it's actually a pretty revolutionary idea. Here are my thoughts on it:
State is generally agreed to be pretty evil and a large source of bugs in programming. Instead of scattering it all throughout your app Redux says why not just have it all concentrated in a global state tree that you have to emit actions to change? Sounds interesting. All programs need state so let's stick it in one impure space and only modify it from within there so bugs are easy to track down. Then we can also declaratively bind individual state pieces to React components and have them auto-redraw and everything is beautiful.
However, I have two questions about this whole design. For one, why does the state tree need to be immutable? Say I don't care about time travel debugging, hot reload, and have already implemented undo/redo in my app. It just seems so cumbersome to have to do this:
case COMPLETE_TODO:
return [
...state.slice(0, action.index),
Object.assign({}, state[action.index], {
completed: true
}),
...state.slice(action.index + 1)
];
Instead of this:
case COMPLETE_TODO:
state[action.index].completed = true;
Not to mention I am making an online whiteboard just to learn and every state change might be as simple as adding a brush stroke to the command list. After a while (hundreds of brush strokes) duplicating this entire array might start becoming extremely expensive and time-consuming.
I'm ok with a global state tree that is independent from the UI that is mutated via actions, but does it really need to be immutable? What's wrong with a simple implementation like this (very rough draft. wrote in 1 minute)?
var store = { items: [] };
export function getState() {
return store;
}
export function addTodo(text) {
store.items.push({ "text": text, "completed", false});
}
export function completeTodo(index) {
store.items[index].completed = true;
}
It's still a global state tree mutated via actions emitted but extremely simple and efficient.
Isn't Redux just glorified global state?
Of course it is. But the same holds for every database you have ever used. It is better to treat Redux as an in-memory database - which your components can reactively depend upon.
Immutability enables checking if any sub-tree has been altered very efficient because it simplifies down to an identity check.
Yes, your implementation is efficient, but the entire virtual dom will have to be re-rendered each time the tree is manipulated somehow.
If you are using React, it will eventually do a diff against the actual dom and perform minimal batch-optimized manipulations, but the full top-down re-rendering is still inefficient.
For an immutable tree, stateless components just have to check if the subtree(s) it depends on, differ in identities compared to previous value(s), and if so - the rendering can be avoided entirely.
Yes it is!!!
Since there is no governance of who is allowed to write a specific property/variable/entry to the store and practically you can dispatch any action from anywhere, the code tends to be harder to maintain and even spaghetti when your code base grows and/or managed by more than one person.
I had the same questions and issues with Redux when I started use it so I have created a library that fix these issue:
It is called Yassi:
Yassi solves the problems you mentioned by define a globally readable and privately writable store. It means that anyone can read a property from the store (such as in Redux but simpler).
However only the owner of the property, meaning the object that declare the property can write/update that property in the store
In addition, Yassi has other perks in it such as zero boilerplate to declare entry in the store by using annotations (use #yassit('someName'))
Update the value of that entry does not require actions/reducers or other such cumbersome code snippets, instead just update the variable like in regular object.
Having spent some time working with flux (both ‘vanilla' and with various frameworks including alt and fluxible) I am left with a question about best practice with regard to loading the initial state of components. More specifically about components directly accessing the store to do it.
The flux ‘model’ prescribes a unidirectional flow of data from Action>Dispatcher>Store>View in a loop, yet it seems this convention is eschewed when loading the initial state of components, most docs/tutorials contain examples where rather than firing an action to get the data, the component calls a function on the store directly (examples below).
It seems to me that components should have little/no information about the store, only about the actions that they can fire, so introducing this link seems both unintuitive and potentially dangerous as it may encourage future developers to jump straight to the store from the component instead of going via the dispatcher. It also runs counter to the ‘Law of Demeter’ which Flux is supposed to adhere very strongly to.
What is best practice for this? Is there a reason that this always seems to be the case? Its quite possible that I have missed out something fundamental, so please let me know if so!
Thanks.
Examples of components calling the store directly.
Flux React example from the fb flux repo example chat app (https://github.com/facebook/flux/tree/master/examples/flux-chat)
MessageSection.react.js
getInitialState: function() {
return getStateFromStores();
},
function getStateFromStores() {
return {
messages: MessageStore.getAllForCurrentThread(),
thread: ThreadStore.getCurrent()
};
}
Another example from the same repo for the TODOapp
(https://github.com/facebook/flux/tree/master/examples/flux-todomvc)
TodoApp.react.js
function getTodoState() {
return {
allTodos: TodoStore.getAll(),
areAllComplete: TodoStore.areAllComplete()
};
}
Example of the alt implementation of the above todo app: (https://github.com/goatslacker/alt/tree/master/examples/todomvc)
TodoApp.js
function getTodoState() {
return {
allTodos: TodoStore.getState().todos,
areAllComplete: TodoStore.areAllComplete()
};
}
and finally the alt specific tutorial:
(https://github.com/goatslacker/alt-tutorial/blob/master/src/components/Locations.jsx)
Locations.js
componentDidMount() {
LocationStore.fetchLocations();
},
It depends on how the structure of you app looks like. Often you want to fetch some data before showing something to the user. What I have found to be a good practice is to have a high end component which fires on mount an action which fetches any initial data from an API. This means that when this action is done fetching it calls the store which caches the data and then emits a change.
This change emit then sets in motion the re-rendering of the whole application.
This way you keep the uni-directional data flow. The whole point with Flux is letting the user extract the data flow functionality out of components to keep them more clean, discourage components to directly communicate with each other and decrease the amount of unnecessary properties which has to be passed around the application.
In the examples the initial state fetches some initial value from the store. This is a common way to fetch the initial value, but you could set it in the component as well. Both ways I would say is a good practice. This does not mean that every component is free to fetch whatever they like from the store.
Anyway, the goal would be to keep the code and data flow as intuitive as possible with Flux. All of this are reasons why there are so many implementations of Flux.