I am updating a part of a page via a standard this.setState
mechanism. I want to grab a hold of a elements that has been changed on a web page and provide a visual feedback to a user.
Let's say we have a component RichText
that gets a data
props. To render rich text it will delegate render to smaller components like Paragraph
, Header
, BulletPoints
, Text
, etc. The final result is a properly rendered rich text.
Later data
props change (e.g. socket push). As a result of that Paragraph
s can be added, or text changed, or things could move around. I want to provide a visual feedback to a user by simply highlighting HTML nodes that were changed.
In a nutshell I want to achieve what Chrome inspector is showing when you are looking at HTML tree. It blinks DOM changes.
ReactJS knows what was changed. Ideally I would like to get an access to that knowledge.
While smaller Components like Paragraph
could be responsible for highlighting a difference within themselves, I don't think they have enough of a knowledge of the outside world to make it work as expected.
Format (simplified version)
{
content: [{
type: 'Document',
content: [{
type: 'Paragraph',
content: [{
type: 'Text',
text: 'text text'
}, {
type: 'Reference',
content: 'text text'
},
]}, {
type: 'BulletPoints',
content: [{
type: 'ListEntry', content: [{
type: 'Paragraph', content: [{
type: 'Text',
text: 'text text'
}, {
type: 'Reference',
content: 'text text'
}]
}]
}]
}]
My current solution
I have a top level Component that knows how to render the entire Document
by delegating job to other components. I have a live version HOC of it: LiveDocument
that is responsible for a change visualization.
So I capture DOM before setState
and after setState
. Then I am using HtmlTreeWalker to spot a first difference (ignoring certain elements as I walk the tree).
React already have an addon for these situations. ReactCSSTransitionGroup
ReactCSSTransitionGroup is a high-level API based on ReactTransitionGroup and is an easy way to perform CSS transitions and animations when a React component enters or leaves the DOM. It's inspired by the excellent ng-animate library.
You can easily animate items that are entering or leaving a specific parent.
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
const nextId = (() => {
let lastId = 0;
return () => ++lastId;
})();
class TodoList extends React.Component {
constructor(props) {
super(props);
this.state = {items: [
{id: nextId(), text: 'hello'},
{id: nextId(), text: 'world'},
{id: nextId(), text: 'click'},
{id: nextId(), text: 'me'}
]};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd() {
const newItems = this.state.items.concat([
{id: nextId(), text: prompt('Enter some text')}
]);
this.setState({items: newItems});
}
handleRemove(toRemove) {
let newItems = this.state.items.filter(item => item.id !== toRemove.id);
this.setState({items: newItems});
}
render() {
const items = this.state.items.map((item) => (
<div key={item.id} onClick={() => this.handleRemove(item)}>
{item.text}
</div>
));
return (
<div>
<button className="add-todo" onClick={this.handleAdd}>Add Item</button>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{items}
</ReactCSSTransitionGroup>
</div>
);
}
}
ReactDOM.render(<TodoList/>, document.getElementById("app"));
.example-enter {
background-color: #FFDCFF;
color: white;
}
.example-enter.example-enter-active {
background-color: #9E1E9E;
transition: background-color 0.5s ease;
}
.example-leave {
background-color: #FFDCFF;
color: white;
}
.example-leave.example-leave-active {
background-color: #9E1E9E;
transition: background-color 0.3s ease;
}
.add-todo {
margin-bottom: 10px;
}
<script src="https://unpkg.com/react@15/dist/react-with-addons.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
<div id="app"></div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With