I'm wondering should I use 'this.props' and 'this.state' inside React class methods or prepare only needed data from state and props outside and pass it to a method as parameters?
First option looks quicker, but maybe more error prone.
Personally I'd treat the component methods quite the same as usual methods in javascript. For me it means that just by looking at the method header I can at least suppose, base on the name and entry params, what the specific method is responsible for.
Second thing is, that it can depend on component shape itself.
Lets imagine that in your component you got the method named 'markPersonAsAbsent'.
If your component just handle single person than in fact can be quite obvious that we should operate on something like this.props.person.
But if your component is more like a list of people which can be inline edited, not just the info card of a single person, than for sure we'd expect to pass a specific person as param to that method.
In that scenario of course we can argue that the components are wrongly designed and should be more atomized, but just wanted to describe simple example.
So personally I just try to focus how to make my components readable and let's say 'predictable'.
It is not error prone, since this.state and this.props are read only properties, so you can't change their values directly.
Related
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 have built my own FormBuilder component (which is almost 5k line now), so it can cover all my needs, one issue i'm encountering is loading a new set of FormData any time the user needs, since my FormBuilder can accept a set of nested components, its kinda hard comparing the changes, and handling everything in component did mount, one easy way is building a loadFormData method inside the FormBuilder and calling this method from outside the component
I know this can easily be accomplished using ref attribute, but react highly suggest avoiding this and i got discouraged! and i was not sure if i should do this or not!
So i came up with a new alternative which kinda does the same thing:
class A {
onGetFormBuilderInternalFunction = (functions) => {
this.formBuilderFunctions = functions
}
onLoadButtonClick = () => {
this.formBuilderFunctions.loadFormData(someNewData)
}
render () {
<FormBuilder onGetInternalFunction={this.onGetFormBuilderInternalFunction}
}
}
class FormBuilder {
componentDidMount() {
if (this.props.onGetInternalFunction) {
this.props.onGetInternalFunction({
loadFormData: this.loadFormData,
})
}
}
}
So what do you think ?, is this a better approach ? or is this still a bad one ?, i was thinking using this method will at least only gives access to functions i need and not everything else.
I should also mention, loadFormData is simply one example, and there are at least a couple of these special function which i really really think is best to simply call from outside, so even if i do somehow pass the new data from props and handle it in componentDidUpdate, for the rest of these functions, i still need to access the FormBuilder
Yes that is the correct way to do it. However, if you have a 5,000 line component I would strongly suggest breaking it up into smaller sub components.
Keep state and handler functions in Parent and pass it down to FormBuilder as props.
I'm not sure how your component is set up so these are just thoughts based on a vague understanding.
Generally, you wouldn't want to do either of those approaches. Instead, the recommended approach is to pass someNewData to FormBuilder as one or more props (I'm guessing one prop, in your case).
Avoid using refs for anything that can be done declaratively.
– React Docs: When to Use Refs
This sort of scenario is discussed in Lifting State Up:
Instead of trying to sync the state between different components, you should rely on the top-down data flow.
– React Docs: Lifting State Up # Lessons Learned
If you're keeping track of changed state separate from your initial data, then you just need to reset that state when the form data (someNewData) changes. However, that's not the recommended approach.
Instead, uncontrolled components might be a great fit. By setting defaultValue to the initial data (i.e. someNewData.someField) and then letting the component deal with the value until you need it (e.g. when submit is clicked read the value from the component), you might be able to simplify your FormBuilder. Note: if you want to reset the uncontrolled component when your initial data (someNewData.someField) changes, you will need to use a key prop on the uncontrolled component with the value based someNewData.someField.
If you want to “reset” some state when a prop changes, consider either making a component fully controlled or fully uncontrolled with a key instead.
– React Docs: React Component # static getDerivedStateFromProps()
In response to your main question:
Better as you are doing right now than using ref.
I base this answer on what the react documentation says in the last paragraph of the "ref" glossary description and that the current approach can also be supported in case FormBuilder is refactored to be a functional component in the future (with Hooks and so), while using "ref" wouldn't.
Should derived data be the responsibility of the NGRX store through selectors or should it be defined on the component that will use it? For example, let's say you have an object with startTime, itemsCompleted, and itemsRemaining. A utility function calculates some other properties such as expectedLate, expectedOverage, etc. Should that take place inside a selector or locally on the component? Does it matter?
Personally, I like to use selectors for derived data. A component just retrieves data from the store and displays it.
This imho makes selectors and components easier to test.
Following this convention it's also easier for devs to find the information they are looking for.
Also, you have one single source of truth. For example, if a user logs out you just have to update the user instead of updating the user and setting a isLoggedIn flag to false.
When passing props should I pass the entire object into the child components or should I individually create props first in the parent component and then pass those props to the child?
Pass entire object:
<InnerComponent object={object} />
Individually create props that're needed first:
<InnerComponent name={object.name} image={object.image} />
Which one is preferred and if it depends, what should I use as a gauge to use either?
According to the principle of least privilege, this is a correct way:
<InnerComponent name={object.name} image={object.image} />
This restricts InnerComponent from accidentally modifying original object or accessing properties that aren't intended for it.
Alternatively, properties could be picked from original object and passed as props:
<InnerComponent {...pick(object, 'name', 'image')} />
If there are numerous properties that are tedious to list, there may be single prop that accepts an object:
<InnerComponent object={pick(object, 'name', 'image')} />
You should prefer your second example, to pass the props individually.
There you can see explicitly which arguments are passed and can quickly compare with which arguments are expected. This makes collaboration along people much easier and makes the state of the application more clear.
I personally try to avoid passing whole objects if it's not needed.
It's like the spread operator where there can be any property inside and it is much harder to debug in a bigger scope.
Some additions to pure react like prop-types or typescript might help to define clear and expected properties in a big application
Since JavaScript has no type annotation, the second choice would be much preferable, as mentioned by #mstruebing the code will be more maintainable. However, if you are using TypeScript React (which support type annotation), both choice would be equally clear. But do keep in mind, the first choice promotes tight coupling between views and models, this allows fast development as you don't have to type much; while the second choice might slows down development but you have higher flexibility and tolerance to future changes.
So, the gauge is: if you favor development speed over flexibility, go for choice 1; if you favor flexibility over development speed, go for choice 2.
Extra notes
Ok, when should we favor development speed over flexibility? Here's my 2 cents.
If your project was meant for short-term usage (for example developing an app just for collecting votes during election), then go for the first choice.
If your project was meant for long-term usage (for example online bank transaction portal), you should go for the second choice.
Passing multiple props to a child component is the preferred way of passing props to child components. This will help you in tracking the state of the application in the child component.
Also, feel free to use the prop-types module. It acts like generics in Java and provides type safety for the props to pass in.
If you aren't making use of all the properties within the object, its better to pass the required properties separately as it can help in performance optimisation when the Child component is a PureComponent of use shouldComponentUpdate. In such a case, only when the props used by the Child component change, its will re-render and not when an unused property from within the object has changed.
However if your are making use of all the properties within the object, it doesn't make much of a difference event if you pass down the object as a props, provided you handle the mutations of the object correctly.
I agree with the other answers that in short, it depends, and the points raised in other answers are all valid.
Some other points I would like to add that also leans towards using individual properties are as follows -
Sometimes its not even the question of whether Do I need all the properties? It should be more of the question - Do I want to restrict the usage of the component to when I only have the complete object and not just a subset of properties.
Take for example a widget to display a car's image, make and model as part of a list of a widgets, its not reasonable to expect the full Car type to be available in order to display only the car's image, make and model. Only those 3 properties are required.
With the likes of rise of the likes of graphql where we can restrict the properties of data being returned about a type, we shouldn't always assume that components will have access to fully populated objects based on a type.
Lastly on the point of causing unnecessary rerenders of components down the tree, always passing the full object around, you are likely to cause all children that consumes that object will rerender when the object is updated. However passing separate properties to children, usually primitives, we can better mitigate against unnecessary rerenders
The whole point of objects is to encapsulate similar properties together. If you have about 10 properties and want to use them in your child component, stick with objects.
But in your case, my opinion is to use your second example as it makes the code in the child component simpler.
To the best of both worlds simply destructure those props that you need inside your child component.
Like this:
function InnerComponent({ name, image }) {
...
}
If you don't like braces in the arguments do this:
function InnerComponent(props) {
const { name, image } = props;
...
}
It depends on that Object. Do you need ALL of its properties to be passed to another component? Or... You might need to pass 2 properties out of 5 to a specific component?
If you don't need to pass ALL of its properties, I recommend passing
the Properties as Props
BUT, if you are 100% sure that you NEED ALL THE PROPERTIES to be
passed together, I recommend passing the whole Object as Props in
that case as it would be much clearer code and keeps you away from
forgotten to pass something. For example; When you add a new property
to that object, you don't have to pass it, as you are already passing
the whole Object!
Well, if you aren't certain about your current/future purpose of that Object as you might need to EXCLUDE some properties soon, you probably should pass its properties individually in that case, instead of passing some non-used properties to the other component.
You can pass props into JSX element as single object using React.createElement function.
Because each JSX element is just syntactic sugar for calling React.createElement.
The following 2 lines are equal for React:
<Button variant={"secondary"}>Add</Button>
{React.createElement(Button, {variant: "secondary", children: "Add"})}
The following is from an issue I posted on EmberJS GitHub, but Stack Overflow is better suited for a discussion than GitHub.
I am building a few complex components at the moment including composite components and I hit a roadblock with the extreme isolation components live in.
There are several cases where I don't need the components to trigger an action on a controller, but where a controller needs to trigger a behaviour change on the component.
Problems is that the components don't know about the controller and the controller is not creating the components either: they are defined in a template.
I kind of solved the problem by subclassing the Ember.Component class to offer a way for messages to get through components.
The new component subclass breaks the on purpose isolation of components that shouldn't know about the outer controller.
The 2 less invasive options I found to make component methods calls from outside are:
Cache component / name of instance pairs in a global array like
App.components, then call a component method with
App.components['name'].method()
Trigger events from outside, register and handle them in the
components. However in that case I am passing an eventSource object
to the component, often a controller like so: {{my-component
eventSource=controller}}
My question is about how could we solve this problem in the most elegant and less invasive way possible for components ?
For achieving composite components, using the components like lego pieces, it seems impossible to me at the moment to see how we can achieve that goal without breaking the components isolation.
Any input, ideas, solutions, discussion is very welcome.
Note: By the way, the first method that breaks the component isolation is inspired by the work done on the ember-bootstrap components: https://github.com/ember-addons/bootstrap-for-ember
The author had to solve the same problem of being capable of triggering methods inside the components from outside.
Another note: Just for the record, another way to access a component is to use Ember.View.views['name'] where name is a view name you gave to your component. However I feel dirty to make such calls, even more from a controller.
In general, I would try to solve this by binding properties to your component, which could then change according to computed properties or observers based on those properties.
For instance, instead of trying to call a method like deactivate on a component, bind a property such as active to a property in the template:
{{my-component active=formEnabled}}
I'm hesitant to recommend passing an eventSource to components as a general solution (like your {{my-component eventSource=controller}} example). I imagine this could work with a class or mixin that just emits specific events and is tightly coupled with your component, but I'm struggling to come up with a use case that justifies this approach.