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.
Related
In a React component I am using an external library that creates complex components that I modify slightly on render. Thus, in my own component, I use things like element queries and mutation observers to get rendered elements in the DOM and dynamically add my own modifications, depending on the state of the DOM itself (not the external component, since I cannot access its state).
This works great, but I have not been able to figure out how to test this functionality [in Jest]. In particular, I add mutation listeners that add my own small React components based on the HTML that the external component renders, adding a button when the mouse hovers over a list of dynamically-created elements. When I attempt to test this in Jest, none of this logic is performed, and the functionality I have added cannot be tested, as the changes to the DOM do not appear, even with full rendering. In particular, during testing I have found that the queries I am using in the component do not have any values, as they need the external component to fully render the HTML for my own component to observe the changes.
I need a way to test my component in a way such that the external component is rendered, but such that my own component can perform updates afterwards, when its own state changes.
How can I test the functionality of the updates that I perform that are based on element queries if there are not any results to these queries during testing?
I am new to React and a bit struggling with state in React and how and where we need to use it. So far, I found out that "If modifying a piece of data does not visually change the component, that data shouldn’t go into state". So, state is all about re-rendering the UI(I hope I am correct). So, the question I want to ask is Is it true that we use state only for re-rendering the UI only?, nothing else and nothing more?
You can use state in your class components. State is like private data of your component that may change by action made by user.
State is immutable. This means you can not change state directly in following way this.state.someVal = "smth". The only way to change state is using this.setState() method.
When you change state value React automatically re-renders your component without refreshing the page. In other words React.js reacts to your changes
State is an object that is directly tied to rendering the component. The reason why you can't change State directly with say this.state.foo='bar' is that React would have no way of knowing that it needed to re-render the component if you did that. Thus there is a setState method to change the state, which under the hood calls the render function of your component.
Therefore, if you have some data that has nothing to do with rendering the component, you don't want to put it into state, as setting its value will cause unnecessary renders to occur. If you're using class components, you can just put that data on the class directly: this.foo='bar'.
Basically yes! Two examples might be: A - holding a list of items (shopping list, or todo items) that are rendered directly to the UI, that are subject to change as the user adds and removes items. B - a value that determines whether or not you want something to show up on your UI, for example, you might have a state value called 'showNavbar' that is either true or false, depending on whether you want the user to see a navigation bar.
I hope that helps make sense of it in a basic way :)
We use the state for rendering the UI.
Also, I think the State allows React components to change their output over time in response to user actions, network responses, and anything else, without violating this rule.
For this, We use the 'setState' method.
setState() is the only legitimate way to update state after the initial state setup
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
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.
I have been using React from couple of months and React doesn't simply re-rendering a component completely instead it finds the difference and makes those changes. Does Angular 2 does something like this?
And also whenever a change in state is detected does Angular 2 re-render all the components from the root node or does it only re-render those specific components and their sub-tree whose change is detected?
React doesn't simply re-rendering a component completely instead it finds the difference and makes those changes. Does Angular 2 does something like this?
Conceptually yes, it does not re-render entire components.
Angular builds a change detector object for each component/directive. Template bindings (which includes input property bindings) are tracked inside these change detector objects. When change detection runs, by default, each binding is dirty checked for changes. If a change is found, the changed value is propagated to the child component (if an input property changed) or to the DOM. That's it. The entire template/view is not re-rendered. Only the changed values are updated in the DOM. When Angular change detection finishes, the browser notices the DOM changes and updates what we see on the screen.
whenever a change in state is detected does Angular 2 re-render all the components from the root node or does it only re-render those specific components and their sub-tree whose change is detected?
Angular doesn't detect changes to some model/data objects. Rather, it only detects changes to template bindings.
By default, each time change detection runs, it starts from the root component and checks all components for changes, in depth-first order, using those change detector objects. As described above, only template bindings with changes are updated. So, I wouldn't say that Angular ever re-renders a component... it only modifies those parts of the DOM where a template binding changed.
You can configure a component to use the OnPush change detection strategy to limit when that component and its descendants are checked for changes. You can also completely detach() a component from the change detector tree, which means that component and its descendants will not be change detected until you reattach().
Angular is not using virtual DOM as React do. No need for that in context of Angular.
If you have <input> and need to set its value in runtime to something else you don't need to change all DOM around it. You just call setValue() on that element and that's it.
The same applies to any other DOM element. For example if you have this:
<div>{{someVar}}</div>
and Angular detects that someVar was changed it will change content of only that particular <div>.
Angular only renders where it detects changes.
AFAIK there is some room for improvments in *ngFor where it sometimes re-renders too many items when some are added/removed in the middle or the beginning but that is a known issue and will be fixed eventually.
From my comment below
In fact Angular doesn't need re-rendering optimization because it only does anything when bound values change and then it only changes the DOM where it is bound to the changed value. Angular doesn't have a virtual DOM that it needs to mirror to the actual DOM.
Angular2 is using zone.js for onChange rendering. Usually when a change is detected, it will trigger changeDetection that component and all the children, but you also can have control to change that, to force render some things or not render when you don't like angular2 behavior.
Here is a very good talk about how Angular2 change detection works : https://www.youtube.com/watch?v=CUxD91DWkGM
LE: Just to clarify, it will not re-render the component and all the children, it will detect and trigger changes for all of those, but will render only what is necessary.