forked from mobxjs/mobx
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreact-optimizations.html
More file actions
163 lines (151 loc) Β· 22.6 KB
/
react-optimizations.html
File metadata and controls
163 lines (151 loc) Β· 22.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>Optimizing React component rendering Β· MobX</title><meta name="viewport" content="width=device-width"/><meta name="generator" content="Docusaurus"/><meta name="description" content="<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CEBD4KQ7&placement=mobxjsorg" id="_carbonads_js"></script>"/><meta name="docsearch:language" content="en"/><meta property="og:title" content="Optimizing React component rendering Β· MobX"/><meta property="og:type" content="website"/><meta property="og:url" content="https://mobx.js.org/index.html"/><meta property="og:description" content="<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CEBD4KQ7&placement=mobxjsorg" id="_carbonads_js"></script>"/><meta property="og:image" content="https://mobx.js.org/img/undraw_online.svg"/><meta name="twitter:card" content="summary"/><meta name="twitter:image" content="https://mobx.js.org/img/undraw_tweetstorm.svg"/><link rel="shortcut icon" href="/img/favicon.png"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css"/><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css"/><script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-65632006-1', 'auto');
ga('send', 'pageview');
</script><script type="text/javascript" src="/js/scripts.js"></script><script type="text/javascript" src="https://buttons.github.io/buttons.js"></script><script src="/js/scrollSpy.js"></script><link rel="stylesheet" href="/css/main.css"/><script src="/js/codetabs.js"></script></head><body class="sideNavVisible separateOnPageNav"><div class="fixedHeaderContainer"><div class="headerWrapper wrapper"><header><a href="/"><img class="logo" src="/img/mobx.png" alt="MobX"/><h2 class="headerTitleWithLogo">MobX</h2></a><div class="navigationWrapper navigationSlider"><nav class="slidingNav"><ul class="nav-site nav-site-internal"><li class=""><a href="/api.html" target="_self">API Reference</a></li><li class=""><a href="https://zh.mobx.js.org" target="_self">δΈζ(ε―»ζ±ηΏ»θ―)</a></li><li class=""><a href="/backers-sponsors.html" target="_self">Sponsors</a></li><li class=""><a href="https://github.com/mobxjs/mobx" target="_self">GitHub</a></li><li class="navSearchWrapper reactNavSearchWrapper"><input type="text" id="search_input_react" placeholder="Search" title="Search"/></li></ul></nav></div></header></div></div><div class="navPusher"><div class="docMainWrapper wrapper"><div class="docsNavContainer" id="docsNav"><nav class="toc"><div class="toggleNav"><section class="navWrapper wrapper"><div class="navBreadcrumb wrapper"><div class="navToggle" id="navToggler"><div class="hamburger-menu"><div class="line1"></div><div class="line2"></div><div class="line3"></div></div></div><h2><i>βΊ</i><span>MobX and React</span></h2><div class="tocToggler" id="tocToggler"><i class="icon-toc"></i></div></div><div class="navGroups"><div class="navGroup"><h3 class="navGroupCategoryTitle">Introduction</h3><ul class=""><li class="navListItem"><a class="navItem" href="/README.html">About MobX</a></li><li class="navListItem"><a class="navItem" href="/about-this-documentation.html">About this documentation</a></li><li class="navListItem"><a class="navItem" href="/installation.html">Installation</a></li><li class="navListItem"><a class="navItem" href="/the-gist-of-mobx.html">The gist of MobX</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">MobX core</h3><ul class=""><li class="navListItem"><a class="navItem" href="/observable-state.html">Observable state</a></li><li class="navListItem"><a class="navItem" href="/actions.html">Actions</a></li><li class="navListItem"><a class="navItem" href="/computeds.html">Computeds</a></li><li class="navListItem"><a class="navItem" href="/reactions.html">Reactions {π}</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">MobX and React</h3><ul class=""><li class="navListItem"><a class="navItem" href="/react-integration.html">React integration</a></li><li class="navListItem navListItemActive"><a class="navItem" href="/react-optimizations.html">React optimizations {π}</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Tips & Tricks</h3><ul class=""><li class="navListItem"><a class="navItem" href="/defining-data-stores.html">Defining data stores</a></li><li class="navListItem"><a class="navItem" href="/understanding-reactivity.html">Understanding reactivity</a></li><li class="navListItem"><a class="navItem" href="/subclassing.html">Subclassing</a></li><li class="navListItem"><a class="navItem" href="/analyzing-reactivity.html">Analyzing reactivity {π}</a></li><li class="navListItem"><a class="navItem" href="/computeds-with-args.html">Computeds with arguments {π}</a></li><li class="navListItem"><a class="navItem" href="/mobx-utils.html">MobX-utils {π}</a></li><li class="navListItem"><a class="navItem" href="/custom-observables.html">Custom observables {π}</a></li><li class="navListItem"><a class="navItem" href="/lazy-observables.html">Lazy observables {π}</a></li><li class="navListItem"><a class="navItem" href="/collection-utilities.html">Collection utilities {π}</a></li><li class="navListItem"><a class="navItem" href="/intercept-and-observe.html">Intercept & Observe {π}</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Fine-tuning</h3><ul class=""><li class="navListItem"><a class="navItem" href="/configuration.html">Configuration {π}</a></li><li class="navListItem"><a class="navItem" href="/enabling-decorators.html">Enabling decorators {π}</a></li><li class="navListItem"><a class="navItem" href="/migrating-from-4-or-5.html">Migrating from MobX 4/5 {π}</a></li></ul></div></div></section></div><script>
var coll = document.getElementsByClassName('collapsible');
var checkActiveCategory = true;
for (var i = 0; i < coll.length; i++) {
var links = coll[i].nextElementSibling.getElementsByTagName('*');
if (checkActiveCategory){
for (var j = 0; j < links.length; j++) {
if (links[j].classList.contains('navListItemActive')){
coll[i].nextElementSibling.classList.toggle('hide');
coll[i].childNodes[1].classList.toggle('rotate');
checkActiveCategory = false;
break;
}
}
}
coll[i].addEventListener('click', function() {
var arrow = this.childNodes[1];
arrow.classList.toggle('rotate');
var content = this.nextElementSibling;
content.classList.toggle('hide');
});
}
document.addEventListener('DOMContentLoaded', function() {
createToggler('#navToggler', '#docsNav', 'docsSliderActive');
createToggler('#tocToggler', 'body', 'tocActive');
var headings = document.querySelector('.toc-headings');
headings && headings.addEventListener('click', function(event) {
var el = event.target;
while(el !== headings){
if (el.tagName === 'A') {
document.body.classList.remove('tocActive');
break;
} else{
el = el.parentNode;
}
}
}, false);
function createToggler(togglerSelector, targetSelector, className) {
var toggler = document.querySelector(togglerSelector);
var target = document.querySelector(targetSelector);
if (!toggler) {
return;
}
toggler.onclick = function(event) {
event.preventDefault();
target.classList.toggle(className);
};
}
});
</script></nav></div><div class="container mainContainer docsContainer"><div class="wrapper"><div class="post"><header class="postHeader"><a class="edit-page-link button" href="https://github.com/mobxjs/mobx/edit/main/docs/react-optimizations.md" target="_blank" rel="noreferrer noopener">Edit</a></header><article><div><span><script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CEBD4KQ7&placement=mobxjsorg" id="_carbonads_js"></script>
<h1><a class="anchor" aria-hidden="true" id="optimizing-react-component-rendering-"></a><a href="#optimizing-react-component-rendering-" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Optimizing React component rendering {π}</h1>
<p>MobX is very fast, <a href="https://twitter.com/mweststrate/status/718444275239882753">often even faster than Redux</a>, but here are some tips to get most out of React and MobX. Most apply to React in general and are not specific to MobX.
Note that while it's good to be aware of these patterns, usually your application
will be fast enough even if you don't worry about them at all.</p>
<p>Prioritize performance only when it's an actual issue!</p>
<h2><a class="anchor" aria-hidden="true" id="use-many-small-components"></a><a href="#use-many-small-components" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Use many small components</h2>
<p><code>observer</code> components will track all values they use and re-render if any of them changes.
So the smaller your components are, the smaller the change they have to re-render. It means that more parts of your user interface have the possibility to render independently of each other.</p>
<h2><a class="anchor" aria-hidden="true" id="render-lists-in-dedicated-components"></a><a href="#render-lists-in-dedicated-components" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Render lists in dedicated components</h2>
<p>The above is especially true when rendering big collections.
React is notoriously bad at rendering large collections as the reconciler has to evaluate the components produced by a collection on each collection change.
It is therefore recommended to have components that just map over a collection and render it, and render nothing else.</p>
<p>Bad:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-keyword">const</span> MyComponent = observer(<span class="hljs-function">(<span class="hljs-params">{ todos, user }</span>) =></span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span>
{user.name}
<span class="hljs-tag"><<span class="hljs-name">ul</span>></span>
{todos.map(todo => (
<span class="hljs-tag"><<span class="hljs-name">TodoView</span> <span class="hljs-attr">todo</span>=<span class="hljs-string">{todo}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span> /></span>
))}
<span class="hljs-tag"></<span class="hljs-name">ul</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>
))
</code></pre>
<p>In the above listing React will unnecessarily need to reconcile all <code>TodoView</code> components when the <code>user.name</code> changes. They won't re-render, but the reconcile process is expensive in itself.</p>
<p>Good:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-keyword">const</span> MyComponent = observer(<span class="hljs-function">(<span class="hljs-params">{ todos, user }</span>) =></span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span>
{user.name}
<span class="hljs-tag"><<span class="hljs-name">TodosView</span> <span class="hljs-attr">todos</span>=<span class="hljs-string">{todos}</span> /></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>
))
<span class="hljs-keyword">const</span> TodosView = observer(<span class="hljs-function">(<span class="hljs-params">{ todos }</span>) =></span> (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">ul</span>></span>
{todos.map(todo => (
<span class="hljs-tag"><<span class="hljs-name">TodoView</span> <span class="hljs-attr">todo</span>=<span class="hljs-string">{todo}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{todo.id}</span> /></span>
))}
<span class="hljs-tag"></<span class="hljs-name">ul</span>></span></span>
))
</code></pre>
<h2><a class="anchor" aria-hidden="true" id="dont-use-array-indexes-as-keys"></a><a href="#dont-use-array-indexes-as-keys" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Don't use array indexes as keys</h2>
<p>Don't use array indexes or any value that might change in the future as key. Generate ids for your objects if needed.
Check out this <a href="https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318">blog post</a>.</p>
<h2><a class="anchor" aria-hidden="true" id="dereference-values-late"></a><a href="#dereference-values-late" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Dereference values late</h2>
<p>When using <code>mobx-react</code> it is recommended to dereference values as late as possible.
This is because MobX will re-render components that dereference observable values automatically.
If this happens deeper in your component tree, less components have to re-render.</p>
<p>Slower:</p>
<pre><code class="hljs css language-javascript"><DisplayName name={person.name} />
</code></pre>
<p>Faster:</p>
<pre><code class="hljs css language-javascript"><DisplayName person={person} />
</code></pre>
<p>In the faster example, a change in the <code>name</code> property triggers only <code>DisplayName</code> to re-render, while in the slower one the owner of the component has to re-render as well. There is nothing wrong with that, and if rendering of the owning component is fast enough (usually it is!), then this approach works well.</p>
<h3><a class="anchor" aria-hidden="true" id="function-props-"></a><a href="#function-props-" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Function props {π}</h3>
<p>You may notice that to dereference values late, you have to create lots of small observer components where each is customized to render a different part of data, for example:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-keyword">const</span> PersonNameDisplayer = observer(<span class="hljs-function">(<span class="hljs-params">{ person }</span>) =></span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">DisplayName</span> <span class="hljs-attr">name</span>=<span class="hljs-string">{person.name}</span> /></span></span>)
<span class="hljs-keyword">const</span> CarNameDisplayer = observer(<span class="hljs-function">(<span class="hljs-params">{ car }</span>) =></span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">DisplayName</span> <span class="hljs-attr">name</span>=<span class="hljs-string">{car.model}</span> /></span></span>)
<span class="hljs-keyword">const</span> ManufacturerNameDisplayer = observer({ car}) => (
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">DisplayName</span> <span class="hljs-attr">name</span>=<span class="hljs-string">{car.manufacturer.name}</span> /></span></span>
)
</code></pre>
<p>This quickly becomes tedious if you have lots of data of different shape. An alternative is to use a function that returns the data that you want your <code>*Displayer</code> to render:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-keyword">const</span> GenericNameDisplayer = observer(<span class="hljs-function">(<span class="hljs-params">{ getName }</span>) =></span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">DisplayName</span> <span class="hljs-attr">name</span>=<span class="hljs-string">{getName()}</span> /></span></span>)
</code></pre>
<p>Then, you can use the component like this:</p>
<pre><code class="hljs css language-javascript"><span class="hljs-keyword">const</span> MyComponent = <span class="hljs-function">(<span class="hljs-params">{ person, car }</span>) =></span> (
<span class="xml"><span class="hljs-tag"><></span>
<span class="hljs-tag"><<span class="hljs-name">GenericNameDisplayer</span> <span class="hljs-attr">getName</span>=<span class="hljs-string">{()</span> =></span> person.name} />
<span class="hljs-tag"><<span class="hljs-name">GenericNameDisplayer</span> <span class="hljs-attr">getName</span>=<span class="hljs-string">{()</span> =></span> car.model} />
<span class="hljs-tag"><<span class="hljs-name">GenericNameDisplayer</span> <span class="hljs-attr">getName</span>=<span class="hljs-string">{()</span> =></span> car.manufacturer.name} />
<span class="hljs-tag"></></span></span>
)
</code></pre>
<p>This approach will allow <code>GenericNameDisplayer</code> to be reused throughout your application to render any name, and you still keep component re-rendering
to a minimum.</p>
</span></div></article></div><div class="docs-prevnext"><a class="docs-prev button" href="/react-integration.html"><span class="arrow-prev">β </span><span>React integration</span></a><a class="docs-next button" href="/defining-data-stores.html"><span>Defining data stores</span><span class="arrow-next"> β</span></a></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#use-many-small-components">Use many small components</a></li><li><a href="#render-lists-in-dedicated-components">Render lists in dedicated components</a></li><li><a href="#dont-use-array-indexes-as-keys">Don't use array indexes as keys</a></li><li><a href="#dereference-values-late">Dereference values late</a><ul class="toc-headings"><li><a href="#function-props-">Function props {π}</a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><a href="/" class="nav-home"><img src="/img/mobx.png" alt="MobX" width="66" height="58"/></a><div><h5>Docs</h5><a href="/README.html#introduction">About MobX</a><a href="/the-gist-of-mobx.html">The gist of MobX</a></div><div><h5>Community</h5><a href="https://github.com/mobxjs/mobx/discussions" target="_blank" rel="noreferrer noopener">GitHub discussions (NEW)</a><a href="https://stackoverflow.com/questions/tagged/mobx" target="_blank" rel="noreferrer noopener">Stack Overflow</a></div><div><h5>More</h5><a class="github-button" href="https://github.com/mobxjs/mobx" data-icon="octicon-star" data-count-href="/facebook/docusaurus/stargazers" data-show-count="true" data-count-aria-label="# stargazers on GitHub" aria-label="Star this project on GitHub">Star</a></div></section></footer></div><script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script><script>
document.addEventListener('keyup', function(e) {
if (e.target !== document.body) {
return;
}
// keyCode for '/' (slash)
if (e.keyCode === 191) {
const search = document.getElementById('search_input_react');
search && search.focus();
}
});
</script><script>
var search = docsearch({
apiKey: '500db32fbdbd53a814f42aafdfa26bd4',
indexName: 'mobxjs',
inputSelector: '#search_input_react'
});
</script></body></html>