Say I have a child component. Let's say I want to change its height based on information that the parent has.
If I get a reference to it. I can change it with
myChild.changeHeight(newHeight);
(see React.js - access to component methods)
or I could change it with
<Child height={newHeight}/>
Both could be changed in the render() method. But which one should I use?
React team gave few scenarios where we should be using refs.
There are a few good use cases for refs:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
Avoid using refs for anything that can be done declaratively.
If there is a possibility to do some functionality even without using refs, then go ahead and do it. In your case, as you said, you have a way to do it by passing a prop. It would be better if we use props itself rather than ref.
Related
I have a react custom component called HomeContent that accepts some props.
To make that animated as per the react-spring documentation I made
const animatedHomeContent = animated(HomeContent);
Then for rendering I did like this:
<animatedHomeContent
id={1}
avatar="O"
image="..."
image_title="Onion"
title=" Medium Sized Onions"
subtitle="November 08, 2020"
desc="Onions from farms of Nasik"
price="74"
quantity="1kg"
/>
But I see the props are not getting passed.
Please tell me how to do it.
It depends on how you want to interact with your component, do you want to use the api or simply supply updated props?
// update by regenerating springProps from some other state
const springProps = useSpring(props)
// update springProps with api
const [springProps, api] = useSpring(() => props)
Updating by regenerating the spring
With the first example, using one of the animated native elements (animated.XXX) you will rerender the animated wrapper every time you want to update the state, because you will pass it a new, updated springProps object every time you update it, which will cause it to rerender. Or... this is only partially true, the individual SpringValue's in the returned object will be the same with both methods so if you pass them as individual props, rerendering would theoretically not be necessary but if you pass them as a whole object (when springProps is a style object for example) it will be a changed object from last render and cause a rerender of the animated wrapper.
When you use a custom element that does not take a ref inside the wrapper instead of one of the native elements, the component will rerender once for every animation frame. This is suboptimal from a performance perspective, but with this strategy, you can use whichever api (prop names) you want for your component, much like you have done.
Here is a sandbox with this solution that works: sandbox
You can observe that the animation works but also that the component rerenders a lot.
Updating via the api
If you want to make it more efficient, and use the api for updating your custom component, you need to adhere to a few rules. When you use the api, react-spring updates the element via a ref on the corresponding DOM element, and therefore, react-spring must understand how to update the element without requiring React to rerender it. This implies:
Your custom element must be able to hold a ref
Your custom element must attach the ref to a DOM element on which you want all animations to take place. Because forwardRef doesn't allow you to add multiple refs, changes cannot take place on multiple DOM elements inside your custom component if you want to wrap it in animated (there are other strategies to solve this, such as NOT wrapping your component in animated and instead use native animated.XXX elements inside your component and pass SpringValues as props).
The property names of your custom component must correspond to the properties you want to update on the DOM element to which the ref its attached. Otherwise react-spring will not understand how to update this element.
Since your element has a lot of custom properties, react-spring won't be able to update your component via the api. It will attempt to do so by simply setting the updated properties on the element to which the ref is attached, but since you need React to map the custom properties to the actual properties on the DOM element, this will fail (no DOM element that I know of have the set of properties that you provide).
As an example of how to accomplish this, here is a sandbox showing the outlines of this strategy: sandbox
Here you can see that the component renders only once and can still be updated. You can also see that the properties used on the custom component wrapped in animated corresponds to props on a div element, enabling react-spring to do exactly what we want (the children of the AnimatedHomeContent is a special prop in React named props.children which react-spring knows how to deal with).
<AnimatedHomeContent style={{ backgroundColor: spring.backgroundColor }}>
{spring.content}
</AnimatedHomeContent>
In the sandbox is also a bad example where other property names are used.
<AnimatedHomeContentBad
backgroundColor={spring.backgroundColor}
content={spring.content}
/>
To get a correct behaviour from this version, React has to process the component to map the input props to the native DOM element props. When updating this version via the api, react-spring does not rerender the component (since it can take a ref) and instead sets the updated properties on the element to which the ref is attached (inspect the element in the console to see for yourself that these props are set after updating). Since the properties are not real properties, nothing updates or animates on the component.
I’m trying to fire a callback passed down from a parent component. Our old pattern for handling this was to call the function in didInsertElement. In Octane, I see that we can use the did-insert modifier but that seems weird for this use case since we’re not updating the DOM element that we’d use to call did-insert. I've also seen onRender being used in a few cases but I don’t see documentation on that and it’s not firing for me. Any suggestions?
For this specific use case, we have a parent component that can have one of many child components. And for each child component we have specific text that gets displayed in the parent component and we want the child component to be the owner of that text.
Your instinct that did-insert isn't the right solution here is, I think, correct. In general, modifiers should only be used when the element they're going to be attached to is used in some way—that is, for managing interactions with the DOM. In general, we prefer one-way data flow otherwise. However, the scenario you've outlined looks similar to a "registration" pattern, where when a child is instantiated, it tells its parent "Hey, I'm here, here's the info you need about me."
There are a couple reasonable options in this case:
Rethink whether the child component should in fact own that data. I don't doubt you have a good reason for the child component owning the data, but the fact that the parent is already responsible for deciding which child to render may suggest that a mapping which includes both the component to render and the associated text could be a good solution in this space. That would cleanly solve this issue.
Given that per your description you currently do want to avoid having the parent own that data, you could also consider having the child yield the data. However, this usually only works if the DOM relationship for it makes sense. If it does, you could choose to do something like this:
{{yield (hash block='data' text=this.theText)}}
<div class='the-component-body'>
{{yield}}
</div>
<ChildComponent as |child|>
{{#if (eq child.block 'data'}}
<h2>{{child.text}}</h2>
{{/if}}
{{child}}
</Child>
(You can see this strategy working here—in particular, see the resulting DOM!)
While that's very powerful, again, it only works if your DOM layout supports it.
Finally, and perhaps most simply for the use case you have, you can (though not necessarily best, as I think the other options above are usually better when available), you can 'register' the value for the component by calling an action passed into your component during the constructor. Now, using a component's constructor this way for component behavior can be a problem in that it only runs once, when the component is first instantiated, and Glimmer and Ember keep the component instance stable and just change the values passed to them over time as much as possible, so if the value you're passing back up depends on the argument you pass to it, the constructor won't work. If it's always stable and does not depend on the arguments to a component, this does work, and it's often appropriate for a 'registration' pattern, where the child component simply needs to pass a single piece of data to the parent when instantiated.
I'm using ember.js(version 3.7) and I bite confused now. Now, I'm working on optimizing the code part for our app. First I'll try to find What are the things are re-render in components? After getting into this topic I bite confused with it.
In ember they used didRender() method to trigger re-rendering things (jQuery DOM Manipulation, Asnyc function loading,etc...). Right now I don't have much idea about re-render in ember. Can someone explain to me re-render in detail? And, please share if you have any resource about re-render in ember.
Thanks in advance.
didRender hook doesn't about triggering a re-render. It is described as the Guide that you shared the link of:
You can leverage this hook to perform post-processing on the DOM of a component after it's been updated.
You might want to do something about sizes or focuses or scrolls. To achieve that you need to wait till your rendering finishes. Because otherwise you cannot get the exact values and positions of the components. For those cases you can use didRender hook.
For example:
- if you want to focus some parts of the view
- if you want to scroll some parts of the view
- if you want to resize some components
- if you want to call a third-party libraries which tries to access DOM element.
etc. You can use this hook.
For sure, if you do something that affects to component's values, it can trigger a re-render. But this is something that you normally shouldn't do.
Let's have one more clarification of re-render:
As components are rendered, re-rendered and finally removed, Ember provides lifecycle hooks that allow you to run code at specific times in a component's life.
(Ref)
Guide says about 3 main phases (Ref):
Initial Render
Re-render
Component Destroy
In here Re-render means, if an argument or a property of a component changes, it starts to re-render itself. For example, think of a person-card component which displays the properties of a person. Such as {{person-card person=model.person}}. Whenever the person parameter changes, the component will re-render.
This question has two parts:
Why do prop types check fail in my react-only scenario?
Why does a material-ui HoC interfere with the type checking?
When making UI components, I make the children unaware of each other, by passing props through React.cloneElement in a unidirectional flow. In my approach, the root component updates its screen size state, and it's children must accept and pass it on to the next child, and they can adjust the values according to content area dimension left for it. The idea is that the the leaf child itself can decide how to render depending on the space left.
In my simplified code example, the WithSize-enhancer informs the root component the full screen size, while the BridgedContent-enhancer informs the leaf component how/if it should render:
https://codesandbox.io/s/92vop4oyr4
It turns out that the root component (EnhancedPrimaryUI) gets its necessary props, passed from either parent or enhancer. It's child's prop type, on the other hand, will fail on page load. Running devtools only reveals what's going on runtime, and looks totally OK:
I really have no idea why it has to be like that! To me it just appears to be React inner workings. My tentative workaround is to add defaultProps, either in every child, or in App.js see second example.
I know about alternative workarounds like passing context or connecting child components to redux, but don't see how such could be motivated in this case.
I get even more confused because I implemented Material-UI, and found out that every child component that is styled with the WithStyles-enhancer magically causes no failed prop types! see third example
I know material-ui uses context to pass only theme/classes into withStyles.js, and claims to not modify the component passed to it.
So what is happening here? Does it effect it indirectly by the order React do things? Is it a feature or is it a bug?
While I still haven't found an explanation to question 1 (why the prop requirement is not fulfilled in spite of having props to seemingly flow nicely), I found there are several ways to ensure the props get there safely:
Add initial JSX props in App.js: <SecondaryUI height={0} width={0} isMobile={false}> BridgedContent height={0} width={0} isMobile={false}/></...
Use initial state from wrapper component (like in PrimaryUI), where the wrapper can be a context-provider. (This could be a clue to question 2)
Use default props
On a sidenote, the intended mechanism can be accomplished much cleaner now using React-hooks. See example: https://codesandbox.io/s/71r7l9ppvj
Is there a way to stop react from removing/changing nodes embedded in a react component.
For example, I have a react component that acts as a container for a non-react component that manages its DOM on its own. Is there a way to mark such components for reactjs, so that it does not modify its DOM?
In my case, I want my react component to be inline-editable by CKeditor, but react always removes/destroys the editor and all the nodes it has added to the DOM, because they were not defined in the react component itself and so it thinks that those elements should not be there.
Any ideas?
If you return false from a shouldComponentUpdate method on your component, then React will step out of the way and the entire reconciliation process will be skipped for that subtree. Of course, this means that you need to manage all DOM mutations yourself in that area and can't take advantage of React.
Take a look at dangerouslySetInnerHTML on https://facebook.github.io/react/tips/dangerously-set-inner-html.html.
This is the method for adding markup that doesn't sticks to React's update methods and also unsupported tags.
This way you can still update your component, while not updating parts of it.