React not updating attribute on re-rendering - reactjs

I have a React element with a name attribute and child text, both of which are taken from the same value in props.
When re-rendering a React component with a different prop value, only the child text is updated, but the name attribute stays the same:
var Inner = React.createClass({
render: function () {
var name = this.props.name;
return ( <div name={name}>{name}</div> );
}
});
React.render(<Inner name="red"/>, document.getElementById('outer'));
// element is now <div name="red" data-reactid=".0">red</div>
React.render(<Inner name="green"/>, document.getElementById('outer'));
// element is now <div name="red" data-reactid=".0">green</div>
As you can see, after the second call to React.render, the name attribute is still red. (see http://jsfiddle.net/chyp9mxL/)
This problem can be resolved by adding a key={name} in the render function, but I don't see why I have to. Aren't keys only needed when we have multiple components? We only have one here.

Is it because div does not recognize name as a valid prop? I remember reading that passing illegal props to native DOM elements may give unexpected results.
Adding key as a prop simply would have given that div an identity. So React could identify that child and update the value properly.
The key here is to understand not everything in the DOM has a
representation in React "Virtual DOM" and, because direct
manipulations of the DOM (like a user changing an value or a
jQuery plugin listening an element) are unnoticed by React, not using
unique and constant keys will end up with React recreating the DOM
node of a component when the key is not constant (and losing any
untracked state in the node) or reusing a DOM node to render another
component when the key is not unique (and tying its state to this
other component).
Says this article. But it's really strange! I hope someone comes up with a proper explanation.

Because div is not a react component, you can just pass props to react components! You need read more about react prop,here is react props documents.

Related

React rerender props change vs local state change

Why the component Demo does not rerender on counter change?
I understand that props.children are equal to the previous ones, however local state changes, so it should rerender. Is changing local state optimized somehow to detect whether some part of the Top component should be rerendered or shouldn`t?
Children props example
In the second example it does rerender, what is the difference between these examples?
Local state change
In the examples you can click on button and see in the console whether Demo is rerendered.
In the first case you're passing the children which is a part of props and essentially on each re-render of Top component, the same children reference is being returned so React detects that and doesn't re-render the Demo component.
In the second case, React will internally execute React.createElement(...) again to create the Demo component and that's a new reference.
Here is a good line to remember this by :-
if a React component returns the exact same element reference in its
render output as it did the last time, React will skip re-rendering
that particular child
I referred this here - https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#component-render-optimization-techniques
This is because passing children argument to the Top component you are passing a reference to this object. React can detect that nothing has changed comparing to the previous version so it will not re-render it.
When you are rendering component using name <Demo /> in your Top component you are rendering a new version of Demo component every time.

Why doesn't my React components list get messed up when using Index as a key prop?

I have found some information online, which is saying that you shouldn't use the index parameter as the key prop when using a map. For example:
{myPersonsState.map( (person, index) => {
return <Person key={index} />
})
According to what I've found, this is not good.
The reason for that, is, whenever you manipulate data in the state (for example, removing the 3rd element in the array), the key for each element, starting from the 3rd position, will definitely change, because each element's key is it's index (which definitely changes), and by that, React will consider the new element's keys as the old ones (before the change).
By that logic, its only logical to think that React will take the new elements starting from the 3rd position, and give them the old keys, which will consider them as the old ones - which will cause React to think it doesn't need to create a new instance of them, and just use the previous elements data to create them.
So, why do I not see that when actually manipulating the state and re-rendering ?
Why does React know that it is a new element on it's own, even though, it has another element's previous key ?
React's key prop gives you the ability to control component instances. Each time React renders your components, it's calling your functions to retrieve the new React elements that it uses to update the DOM. If you return the same element types, it keeps those components/DOM nodes around, even if all* the props changed.
Key prop allows you to return the exact same element type, but force React to unmount the previous instance, and mount a new one. This means that all state that had existed in the component at the time is completely removed and the component is "reinitialized" for all intents and purposes. For components, this means that React will run cleanup on effects (or componentWillUnmount), then it will run state initializers (or the constructor) and effect callbacks (or componentDidMount).

How are function calls and component calls different in React?

https://codesandbox.io/s/cocky-silence-486sh?file=/src/App.js
In the above example, we see that calling the ChildWrapper as ChildWrapper() vs <ChildWrapper/> causes some differences.
The function method behaves as intended, so when I increment the counter, the reference stays the same, however using the component call, it actually remounts.
I was wondering if my intuition is correct, that the references are changing, and more important why are they changing. And to add on to that, which should be the preferred method for rendering functions that returns jsx, <Component/> or { Component() }.
When you do <ChildWrapper/> it is translated to React.createElement(ChildWrapper, {}, null) which means you ask react to create an element for you.
When you do ChildWrapper() you are just calling a function, react does not know about this and cannot know about it, so you won't have any of react's features (hooks).
With your example, with logging the two results in the console you will see this, and you can notice that the difference is noticeable:
When calling the function, it will return a <div>, here, jsx will transpiles it to an object with type div.
When calling it via jsx from the start, it will return an object with type ChildWrapper.
PS: you are creating the ChildWrapper inside a component (during render) So it will have a new value each time your Parent component renders, and react after rendering and going into reconcialiation, it will remove the whole previous tree and create a new one, because the type changed.
Let's sum up all what I've wrote:
The child "behaving correctly for you" is the function call, because you basically call it and it returns a div, react after render and during reconciliation, finds a div and then just updates it if there is a change. So the mount useEffect(() => ..., []) will not be executed.
Why the child created with jsx isn't behaving correclty ? because you are dynamically creating its type, so at each render, it will create a new ChildWrapper Type, and react during reconciliation will remove it entirely because its type changed, and thus, the mount useEffect(() => ..., []) will be executed each time, because every time we mount a new element.
Read more about reconciliation in this link
From the official documentation :
Each JSX element is just syntactic sugar for calling
React.createElement(component, props, ...children). So, anything you
can do with JSX can also be done with just plain JavaScript.
Internally, the code generated looks like this: React.createElement(Component, { props }, children),
The createElement function will then register and render the component you gave to it, bind it with the shadow DOM and update it whenever it is necessary. It will also pass its props and children arguments.
But if you call your component directly, none of that happens, your component will not be correctly rendered and updated by react because it lacks the core features given by createElement.

What does the second parameter in ReactDOM.render mean?

I've just started learning React and in the ReactDOM.render there is document.getElementById('someid'), what does it do?
It's the container to which the contents of the first parameter will be rendered to.
Check out public/index.html in your project if you use create-react-app. You should be able to find the <div> with an ID of root (the default value for create-react-app). You can change the ID of that div, but you will have to change the ID in the getElementById call in your index.js.
Here's what the documentation says about it: https://reactjs.org/docs/react-dom.html#render
ReactDOM.render(element, container[, callback])
Render a React element into the DOM in the supplied container and
return a reference to the component (or returns null for stateless
components).
If the React element was previously rendered into container, this will
perform an update on it and only mutate the DOM as necessary to
reflect the latest React element.

React setState on array of objects rerenders every component

I'm using React, and want to update array of objects in a state. The overall answer in internet was to use a similar approach to below:
const newState = this.state.elements.slice().concat({key: 'val'});
this.setState({elements: newState});
But the problem I encountered with this issue is that when these data are binded into a render function and components, it re-renders every component.
Example include (map is used from lodash to retrieve index while mapping):
render() {
return (
<div>
{map(this.state.elements, (el, index) => <Component key={`el-${index}`} el={el} />)}
</div>
);
}
Even though, the array order doesn't change and the key's of the components doesnt change, everytime the state changes, it re-renders and mounts the component from scratch.
Is there a possible best practice solution to this issue?
Best,
Y
Whats happening is that when you call setState the React Virtual Dom diffs state and calls render again. Render calls to a Map function which renders each component. Each component is thrown away and redrawn because the parent component changes.
This is normal and expected behavior by React. It's doing what you're asking of it, you changed state, it redraws the components.
But your question is how do I append to the list of components my new component without redrawing?
I think the issue is that you're relying on the unique key of react to be the index of the array item.
<Component key={`el-${index}`}
That will forever change every time you update state. What you need to do is use something like name, or string or a generated key or something from the data.
You are using just map function index value and of course after adding new element to the list will trigger a re-render because key value for each of them was changed.
if you did something like this:
<Component key={`el-${item.id}`}
Where item id is a constant but unique value, like a primary key in a database table. It would not redraw. The key should have a deterministic value.
See React Docs and this other post for more details.

Resources