Highlighting value change ReactJS - javascript

I am using ReactJS, and I was wondering whether it is possible to highlight an element in the DOM when its value has been changed.
I have a list of elements whose values update periodically. While I have no problem animating in the DOM new items coming in to the list or items leaving the list using React's animation library interface, I am struggling to figure out how to detect the actual value change of the existing elements in the list.
Any ideas on how to do so? Thank you!

I had the same problem, then I decided to create a generic small module to handle it everywhere, that you can install from here
https://www.npmjs.com/package/react-change-highlight
using
yarn add react-change-highlight
what you can do to use it is to wrap you part that you want to highlight inside the component itself as follow
import ChangeHighlight from 'react-change-highlight';
export default () => {
const [count, setCount] = useState(0);
return (
<ChangeHighlight>
<div ref={React.createRef()}>{count}</div>
</ChangeHighlight>
);
}
and you will find the highlight on change as in the example below
I hope you find this useful

If your new values are coming down as new props, then you can use the componentWillReceiveProps lifecycle method on the component. It is called just before the component is updated.
Within that method, you can compare the new values (passed as the argument to the method) and the old values (available as this.props).
For instance:
componentWillReceiveProps: function(newProps) {
if(this.props.query != newProps.query) {
//handle change of query prop
//may include calls to this.setState()
}
}

Assuming that your elements are input elements, you need to use the onChange event listener. If you want to highlight that element, I would suggest having a CSS class that highlights the element and then conditionally applying it in render() based on state (or props if a child).
So for instance, you can have one component, add a handler function and the appropriate listener in the render():
handleChange: function(evt) {
//input was changed, update component state (or call parent method if child)
this.setState({changed: true});
},
render: function() {
return (<div>
<input type="text"
className={this.state.changed ? 'highlight' : ''}
onChange={this.handleChange}
/>
</div>);
}
That should set you in the right direction.

Related

Update list display in Next.js when passing data from children

I have a Page component in Next.js that looks somewhat like this:
export default function Index({ containers }) {
const [containerListState, setContainerListState] = useState(containers);
const updateContainerList = (container) => {
containers.push(container);
setContainerListState(containers);
};
return (
<>
<FormCreateContainer updateContainerList={updateContainerList} />
<ContainerList containers={containerListState} />
</>
);
}
FormCreateContainer allows for the creation of a container. When that API call resolves, I call updateContainerList and pass the newly created container to the parent. This works.
However, I also want to pass this container to ContainerList (which is a simple dynamic list using containers.map()) as part of the containers prop. While pushing to containers works as intended, Next does not live-update my list of containers; the newly created container only shows up when I reload the page.
I thought that including useEffect and changing updateContainerList as follows might work, but alas it did not.
const updateContainerList = (container) => {
containers.push(container);
};
useEffect(() => {
setContainerListState(containers);
}, [containers]);
How do I correctly pass data from a child to a parent component, therethrough passing it to a different child component and updating a dynamic list without reloading the page myself?
I really do appreciate any help as I have done extensive research that did not help me achieve my goal.
First and foremost, you should never mutate the value of a prop.
I think you should just use setContainerListState to update the data without mutating the prop like this:
const updateContainerList = (container) => {
setContainerListState(containers => [...containers, container]);
};
This will re-render your component and all its children automatically.

React best practice for changing state of parent component from child without rerendering all children?

I have a project where I'm displaying cards that contain attributes of a person in a textfield, and the user can edit the textfield to directly change that person's attribute values. However every time they're editing the textfield it causes a rerender of all cards which slows down the app. Here is an example:
export default Parent() {
const [personList, setPersonList] = useState(/* list of person objects*/);
const modifyPerson(index, property, value) {
const newPersonList = _.cloneDeep(personList);
newPersonList[index][property] = value;
setPersonList(newPersonList);
}
const children = personList.map((person, index) => {
<Person
modifyPerson={modifyPerson}
index=index
/*properties on the person */
/>
});
return <div> {children} </div>
}
export default Person(props) {
const fields = /* assume a list of these textfields for each property */
<TextField
value={props.name}
onChange={(e) => modifyPerson(props.index,"name",e.target.value)}
value={props.name} >
return {fields};
}
So essentially when the child's text field is updated, it triggers a state change in the parent that stores the new value, then refreshes what the Child looks like. There's no button that the user clicks to "save" the values-- as soon as they edit the textfield it's a permanent change. And also the parent needs to know the new values of every Person because there's some functions that require knowledge of the current state of the person list. The Person component contains images that slow down the rendering if done inefficiently.
Is there a better way to make this design more performant and reduce rerenders? I attempted to use useCallback to preserve the functions but I don't it works in this specific design because the property and values are different-- do I have to create a new "modifyPerson" for each exact attribute?
Use React.memo()
React.Memo will check the props passed to the component and only if the props changes , it will re-render that particular component.
So, if you have multiple Person component which are getting props which are explicit to those Person, and when you change a particular Person component which leads to Parent state getting updated, only that Person component which you had modified will re-render because its props will change(assuming that you pass the changed value to it).
The other components will have the same props and wont change.
export default React.memo(Person(props) {
const fields = /* assume a list of these textfields for each property */
<TextField
value={props.name}
onChange={(e) => modifyPerson(props.index,"name",e.target.value)}
value={props.name} >
return {fields};
})
As others have already said React.memo() is the way to go here, but this will still re-render because you recreate modifyPerson every time. You can use useCallback to always get the same identity of the function, but with your current implementation you would have to add personList as dependency so that doesn't work either.
There is a trick with setPersonList that it also accepts a function that takes the current state and returns the next state. That way modifyPerson doesn't depend on any outer scope (expect for setPersonList which is guaranteed to always have the same identity by react) and needs to be created only once.
const modifyPerson = useCallback((index, property, value) {
setPersonList(currentPersonList => {
const newPersonList = _.cloneDeep(currentPersonList);
newPersonList[index][property] = value;
return newPersonList;
})
}, []);

React re-render via setState vs direct DOM manipulation?

I have been working with react for about 6 months now and something that always used to bother me is the way re-renders work.
Below is a traditional component that has one input box and sends data to the server to whose value is used by some other forms along with multiple almost static HTML elements that are never used or change very rarely. I am saying very rarely because static elements can be built and stored in a variable in the componentWillMount() method. But for this question to be a little more than that, render should contain a call to buildComplexHTMLFromData method.
buildComplexHTMLFromData = (data) => {
// Lot of javascript to build the boxes based on the initial or data
// that changes so rarely
// array.map.filter.find etc.
return (
<div>
//Complex HTML element 1
//Complex HTML element 2
//....
//....
//Complex HTML element n
</div>
)
}
sendDataToBackend = (event) => {
this.setState(
{ value: event.target.value },
() => this.functionThatSendsDataToBackend()
)
}
render() {
<div>
// Active input that sends data to the backend
<input
value={this.state.value}
onChange={this.sendDataToBackend}
/>
{this.buildComplexHTMLFromData()}
</div>
}
Now setting state upon input box change will trigger even the buildComplexHTMLFromData method that does complex javascript all over again. I heard React does something smart by diffing across DOM to efficiently re-render but this javascript is executed anyway.
On the other hand the same functionality can be achieved using two varieties of sendDataToBackend method as shown in the snippet below. This however ensures that only the target input element is changed without touching the already rendered elements or executing any javascript on buildComplexHTMLFromData method.
buildComplexHTMLFromData = (data) => {
// Lot of javascript to build the boxes based on the initial or data
// that changes so rarely
// array.map.filter.find etc.
return (
<div>
//Complex input box 1
//Complex input box 2
//....
//....
//Complex input box n
</div>
)
}
sendDataToBackend = (event) => {
//First strategy
var element = document.getElementById("persistable-input");
element && element.value = event.target.value
//Second strategy
this.persistableInput.value = event.target.value
}
render() {
<div>
// Active input that sends data to the backend or for other forms
<input
id="persistable-input"
ref={(elem) => { this.persistableInput = elem }}
value={this.state.value}
onChange={this.props.persistedValue}
/>
{this.buildComplexHTMLFromData()}
</div>
}
I don't know if I am missing something or if this is very minimal on performance but I feel it could be quite taxing for complex components. I looked multiple articles on React's reconciliation paradigm but it does not seem to address this.
I would really appreciate if anyone could shed some light into this area of React because I am looking for some cool tips and inputs on performant reconciliation in React in most cases.
Thanks in advance.
This is exactly what the shouldComponentUpdate lifecycle hook was created for. If you know that your component shouldn't always re-render, then you can add this lifecycle hook to detect which piece of state is changing. If it something that you don't care about, you can return false and the buildComplexHTMLFromData function won't ever run.
EDIT:
They also expose a base class called PureComponent that handles shouldComponentUpdate under the hood for you.

React adding child outside render()

The title could be clearer, but this is really the best I could come up with, sorry.
So, I am trying to create a filtered table component in React. However, I want the filter to be defined independently from the definition of the table itself. So, here is what I am doing.
I created a Filter component:
var Filter = React.createClass({
handleChange : function (value) {
this.props.updateTable(this.props.columnName,value);
},
render : function () {
//an input that will report its value to this.handleChange
}
});
Then, I create a Table component:
var Table = React.createClass({
filterChanged : function (column, value) {
//this will be wired as a updateTable prop for the Filter
},
render : function () {
//I am trying not to define a filter here,
//I am trying to use the previously-defined Filter component.
//I want the Table component to remain generic and re-usable,
//with optional filters.
var thisComponent = this;
//I can have as many filters as I want.
var filterToRender = React.Children.map(this.props.children, function (child) {
var filterUI;
if (child.type.displayName === 'Filter') {
filterUI = React.cloneElement(child, {updateTable : thisComponent.filterChanged});
}
return (<div>{filterUI}</div>);
});
//along with the rest of the table UI,
return (<div>
<table>bla bla</table>
{filterToRender}
</div>);
}
});
Then, in my main page, I render it like this:
ReactDOM.render( (<Table>
<Filter columnName='status'></Filter>
</Table>), document.getElementById('appHolder'));
It renders fine. The change functions also seem to be wired fine. However, I find that every time the filter value is changed, it triggers the Table's filterChanged method, increasing number of times. First change, it will trigger 2 times; second change, 6 times; 3rd change, 14 times.
Weird and uncanny. What am I doing wrong here?
The above procedure for doing things is correct as far as React is concerned. The bug I was getting was due to another framework (Materialize) that uses a jquery plugin to initialize some components. Since it mutates the DOM, I need to manually make sure the onChange events are properly attached to the right node.
Fwiw, I was attaching the onChange to this.handleChange in a drop-down list in the ComponentDidMount and ComponentDidUpdate of the Filter component. Removing initialization from ComponentDidUpdate, solved the problem. This, because multiple instances of handleChange were being bound to the onChange event each time the component updated.

setState vs refs in react.js

I created tabs in react and and now on click I have to change the class of the tabs the tabs classes may be as follows:
1:active
2:previousActive
3:alreadySelected
On click of a tab class become active and check whether it is selected before or not using alreadySelected class and active class from the last active tab is remove and if it is not alreadySelected then add alreadySelected.
Code of one tab in react:
var TabBody = React.createClass({
getInitialState: function() {
return {
class: 'tabBody tab activeTab'
}
},
render: function() {
a.tabBody = this;
return (React.createElement('div', {
className: this.state.class,
ref: 'body',
onClick: handleTabClick
},
React.createElement('span', {}, "Body"))
);
}
});
In order to change the class of the tabs I am doing in two ways and want to know which is effective. Code style one:
var bodyClass = (a.tabBody.state.class).split(' ');
var sleeveClass = (a.tabSleeve.state.class).split(' ');
var neckClass = (a.tabNeck.state.class).split(' ');
if (data === 'tabBody') {
bodyClass.push('activeTab');
var str1 = program.arrayToString(bodyClass);
Interfaces.tabBody.setState({
class: str1
});
}
Code Style 2
a.tabBody.refs.body.classList.remove('activeTab');
a.tabBody.refs.body.classList.add('tabPreviewComplete');
a.tabSleeve.refs.body.classList.add('activeTab');
Which style is good for doing this and why?
The point of react is that you do not need to/ should not update DOM directly. The idea behind react is that you render react components (virtual DOM), and that you let react figure out if and how to update DOM.
Changing classes using refs is a very risky strategy: Your component's state is then no longer in sync with actual DOM, which could bring you into debugging nightmares later on. So I would pose that Code Style 2 (even though it works) violates react principles.
One of the few exceptions for using refs, is to add a listener to a DOM component after it is mounted.
The react way is to put the classNames in state.
And do a setState() to update.
And let react do the DOM update,
which is very likely to be way faster, cleaner, and easier to maintain than getting refs, and changing classNames.
ref means you are using the actual DOM and setState means you are saying to react that please update the specific attribute of the component.
every thing is maintain by react.
On the other hand if you use refs it means you are doing every thing your own and react have no concern to your attributes and properties you are updating.

Categories