--- id: flux-overview title: Flux Application Architecture  next: flux-todo-list.html  ---  Flux is the application architecture that Facebook uses for building client-side web applications.  It complements React's composable view components by utilizing a unidirectional data flow.  It's more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code.

 Flux applications have three major parts: the dispatcher, the stores, and the views (React components).  These should not be confused with Model-View-Controller.  Controllers do exist in a Flux application, but they are controller-views — views often found at the top of the hierarchy that retrieve data from the stores and pass this data down to their children.  Additionally, actions — dispatcher helper methods — are often used to support a semantic dispatcher API.  It can be useful to think of them as a fourth part of the Flux update cycle. Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with a React view, the view propagates an action through a central dispatcher, to the various stores that hold the application's data and business logic, which updates all of the views that are affected. This works especially well with React's declarative programming style, which allows the store to send updates without specifying how to transition views between states. We originally set out to deal correctly with derived data: for example, we wanted to show an unread count for message threads while another view showed a list of threads, with the unread ones highlighted. This was difficult to handle with MVC — marking a single thread as read would update the thread model, and then also need to update the unread count model.  These dependencies and cascading updates often occur in a large MVC application, leading to a tangled weave of data flow and unpredictable results. Control is inverted with stores: the stores accept updates and reconcile them as appropriate, rather than depending on something external to update its data in a consistent way. Nothing outside the store has any insight into how it manages the data for its domain, helping to keep a clear separation of concerns. This also makes stores more testable than models, especially since stores have no direct setter methods like `setAsRead()`, but instead have only an input point for the payload, which is delivered through the dispatcher and originates with actions. ## Structure and Data Flow Data in a Flux application flows in a single direction, in a cycle:
 Views ---> (actions) ----> Dispatcher ---> (registered callback) ---> Stores -------+ Ʌ                                                                                | |                                                                                V +-- (Controller-Views "change" event handlers) ---- (Stores emit "change" events) --+
 A unidirectional data flow is central to the Flux pattern, and in fact Flux takes its name from the Latin word for flow. In the above diagram, the dispatcher, stores and views are independent nodes with distinct inputs and outputs. The actions are simply discrete, semantic helper functions that facilitate passing data to the dispatcher. All data flows through the dispatcher as a central hub.  Actions most often originate from user interactions with the views, and are nothing more than a call into the dispatcher.  The dispatcher then invokes the callbacks that the stores have registered with it, effectively dispatching the data payload contained in the actions to all stores.  Within their registered callbacks, stores determine which actions they are interested in, and respond accordingly.  The stores then emit a "change" event to alert the controller-views that a change to the data layer has occurred.  Controller-views listen for these events and retrieve data from the stores in an event handler.  The controller-views call their own `render()` method via `setState()` or `forceUpdate()`, updating themselves and all of their children. This structure allows us to reason easily about our application in a way that is reminiscent of functional reactive programming, or more specifically data-flow programming or flow-based programming, where data flows through the application in a single direction — there are no two-way bindings. Application state is maintained only in the stores, allowing the different parts of the application to remain highly decoupled. Where dependencies do occur between stores, they are kept in a strict hierarchy, with synchronous updates managed by the dispatcher. We found that two-way data bindings led to cascading updates, where changing one object led to another object changing, which could also trigger more updates. As applications grew, these cascading updates made it very difficult to predict what would change as the result of one user interaction. When updates can only change data within a single round, the system as a whole becomes more predictable. Let's look at the various parts of the Flux update cycle up close. A good place to start is the dispatcher. ### A Single Dispatcher The dispatcher is the central hub that manages all data flow in a Flux application. It is essentially a registry of callbacks into the stores. Each store registers itself and provides a callback. When the dispatcher responds to an action, all stores in the application are sent the data payload provided by the action via the callbacks in the registry. As an application grows, the dispatcher becomes more vital, as it can manage dependencies between stores by invoking the registered callbacks in a specific order.  Stores can declaratively wait for other stores to finish updating, and then update themselves accordingly. ### Stores Stores contain the application state and logic. Their role is somewhat similar to a model in a traditional MVC, but they manage the state of many objects — they are not instances of one object. Nor are they the same as Backbone's collections. More than simply managing a collection of ORM-style objects, stores manage the application state for a particular __domain__ within the application. For example, Facebook's [Lookback Video Editor](https://facebook.com/lookback/edit) utilized a TimeStore that kept track of the playback time position and the playback state. On the other hand, the same application's ImageStore kept track of a collection of images.  The TodoStore in our [TodoMVC example](https://github.com/facebook/flux/tree/master/examples/flux-todomvc) is similar in that it manages a collection of to-do items.  A store exhibits characteristics of both a collection of models and a singleton model of a logical domain. As mentioned above, a store registers itself with the dispatcher and provides it with a callback. This callback receives the action's data payload as a parameter. The payload contains a type attribute, identifying the action's type. Within the store's registered callback, a switch statement based on the action's type is used to interpret the payload and to provide the proper hooks into the store's internal methods. This allows an action to result in an update to the state of the store, via the dispatcher. After the stores are updated, they broadcast an event declaring that their state has changed, so the views may query the new state and update themselves. ### Views and Controller-Views React provides the kind of composable views we need for the view layer. Close to the top of the nested view hierarchy, a special kind of view listens for events that are broadcast by the stores that it depends on. One could call this a controller-view, as it provides the glue code to get the data from the stores and to pass this data down the chain of its descendants. We might have one of these controller-views governing any significant section of the page. When it receives the event from the store, it first requests the new data it needs via the stores' public getter methods. It then calls its own `setState()` or `forceUpdate()` methods, causing its `render()` method and the `render()` method of all its descendants to run. We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage. Occasionally we may need to add additional controller-views deeper in the hierarchy to keep components simple.  This might help us to better encapsulate a section of the hierarchy related to a specific  data domain.  Be aware, however, that controller-views deeper in the hierarchy can violate the singular flow of data by introducing a new, potentially conflicting entry point for the data flow.  In making the decision of whether to add a deep controller-view, balance the gain of simpler components against the complexity of multiple data updates flowing into the hierarchy at different points.  These multiple data updates can lead to odd effects, with React's render method getting invoked repeatedly by updates from different controller-views, potentially increasing the difficulty of debugging. ### Actions The dispatcher exposes a method that allows a view to trigger a dispatch to the stores, and to include a payload of data, or an action. The action construction may be wrapped into a semantic helper method which sends the payload to the dispatcher. For example, we may want to change the text of a to-do item in a to-do list application. We would create an action with a function signature like `updateText(todoId, newText)` in our `TodoActions` module. This method may be invoked from within our views' event handlers, so we can call it in response to a user action. This action method also adds the action type to the payload, so that when the payload is interpreted in the store, it can respond appropriately to a payload with a particular action type. In our example, this type might be named something like `TODO_UPDATE_TEXT`. Actions may also come from other places, such as the server. This happens, for example, during data initialization. It may also happen when the server returns an error code or when the server has updates to provide to the application. We'll talk more about server actions in a future article. In this post we're only concerned with the basics of the data flow. ### What About that Dispatcher? As mentioned earlier, the dispatcher is also able to manage dependencies between stores. This functionality is available through the `waitFor()` method within the Dispatcher class.  We did not need to use this method within the extremely simple [TodoMVC application](https://github.com/facebook/flux/tree/master/examples/flux-todomvc), but we have included it [in the example dispatcher](https://github.com/facebook/flux/blob/master/examples/flux-todomvc/js/dispatcher/Dispatcher.js#L110) as an example of what a dispatcher should be able to do in a larger, more complex application. Within the TodoStore's registered callback we could explicitly wait for any dependencies to first update before moving forward: ```javascript case 'TODO_CREATE': Dispatcher.waitFor([ PrependedTextStore.dispatcherIndex, YetAnotherStore.dispatcherIndex ], function() { TodoStore.create(PrependedTextStore.getText() + ' ' + action.text); TodoStore.emit('change'); }); break; ``` The arguments for `waitFor()` are an array of dispatcher registry indexes, and a final callback to invoke after the callbacks at the given indexes have completed.  Thus the store that is invoking `waitFor()` can depend on the state of another store to inform how it should update its own state. A problem arises if we create circular dependencies. If Store A waits for Store B, and B waits for A, then we'll have a very bad situation on our hands. We'll need a more robust dispatcher that flags these circular dependencies with console errors, and this is not easily accomplished with promises. Unfortunately, that's a bit beyond the scope of this documentation. In the future we hope to cover how to build a more robust dispatcher and how to initialize, update, and save the state of the application with persistent data, like a web service API.   This page has been moved to the Flux website. [View it there](http://facebook.github.io/flux/docs/overview.html).


}); }); <ins> it('does change the string ".98" to "0.98" with no change handler', () => { class Stub extends React.Component { state = { value: '.98', }; render() { return <input type="number" value={this.state.value} />; } } var stub = ReactTestUtils.renderIntoDocument(<Stub />); var node = ReactDOM.findDOMNode(stub); stub.setState({value: '0.98'}); expect(node.value).toEqual('0.98'); }); </ins> it('should display `defaultValue` of number 0', () => { var stub = <input type="text" defaultValue={0} />; stub = ReactTestUtils.renderIntoDocument(stub);


</span> {typeof overrideValueFn === 'function' ? ( <EditableValue <del> dataType={type} </del> <ins> initialValue={value} </ins> overrideValueFn={overrideValueFn} path={[]} <del> value={value} </del> /> ) : ( // $FlowFixMe Cannot create span element because in property children


type EditableValueProps = {| className?: string, <del> dataType: string, </del> initialValue: any, overrideValueFn: OverrideValueFn, path: Array<string | number>,


'use strict'; var ReactChildren = require('ReactChildren'); <ins> var ReactDOMComponentTree = require('ReactDOMComponentTree'); </ins> var ReactDOMSelect = require('ReactDOMSelect'); var warning = require('warning');


utils.catchup(openingElement.range[0], state, trimLeft); <del> if (nameObject.namespace) { throw new Error( 'Namespace tags are not supported. ReactJSX is not XML.'); </del> <ins> var isFallbackTag = false; if (nameObject.type === Syntax.XJSIdentifier) { if (nameObject.namespace) { throw new Error( 'Namespace tags are not supported. ReactJSX is not XML.'); } isFallbackTag = FALLBACK_TAGS.hasOwnProperty(nameObject.name); </ins> } <del> var isFallbackTag = FALLBACK_TAGS.hasOwnProperty(nameObject.name); utils.append( (isFallbackTag ? jsxObjIdent + '.' : '') + (nameObject.name) + '(', state ); </del> <ins> utils.append(isFallbackTag ? jsxObjIdent + '.' : '', state); utils.move(nameObject.range[0], state); utils.catchup(nameObject.range[1], state); </ins> <del> utils.move(nameObject.range[1], state); </del> <ins> utils.append('(', state); </ins> var hasAttributes = attributesObject.length;


return () => refCallback(null); } else if (ref !== null && ref !== undefined) { const refObject = ref; <ins> if (__DEV__) { warning( refObject.hasOwnProperty('current'), 'Expected useImperativeHandle() first argument to either be a ' + 'ref callback or React.createRef() object. Instead received: %s.', 'an object with keys {' + Object.keys(refObject).join(', ') + '}', ); } </ins> const inst = create(); refObject.current = inst; return () => {


nextEffect = nextEffect.nextEffect; } <ins> if (__DEV__) { resetCurrentFiber(); } </ins> } function commitPassiveEffects(root: FiberRoot, firstEffect: Fiber): void {


 /** * marked - a markdown parser * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) * https://github.com/chjj/marked */ (function(){var block={newline:/^n+/,code:/^( {4}[^n]+n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:n+|$)/,heading:/^ *(#{1,6}) *([^n]+?) *#* *(?:n+|$)/,nptable:noop,lheading:/^([^n]+)n *(=|-){2,} *(?:n+|$)/,blockquote:/^( *>[^n]+(n(?!def)[^n]+)*n*)+/,list:/^( *)(bull) [sS]+?(?:hr|def|n{2,}(?! )(?!1bull )n*|s*$)/,html:/^ *(?:comment|closed|closing) *(?:n{2,}|s*$)/,def:/^ *[([^]]+)]: *<?([^s>]+)>?(?: +["(]([^n]+)[")])? *(?:n+|$)/,table:noop,paragraph:/^((?:[^n]+n?(?!hr|heading|lheading|blockquote|tag|def))+)n*/,text:/^[^n]+/};block.bullet=/(?:[*+-]|d+.)/;block.item=/^( *)(bull) [^n]*(?:n(?!1bull )[^n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","n+(?=1?(?:[-*_] *){3,}(?:n+|$))")("def","n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)b)w+(?!:/|[^ws@]*@)b";block.html=replace(block.html)("comment",//)("closed",/<(tag)[sS]+?</1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(S+)? *n([sS]+?)s*1 *(?:n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("1","2")+"|"+block.list.source.replace("1","3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(S.*|.*)n *([-:]+ *|[-| :]*)n((?:.*|.*(?:n|$))*)n*/,table:/^ *|(.+)n *|( *[-:]+[-| :]*)n((?: *|.*(?:n|$))*)n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/rn|r/g,"n").replace(/t/g,"    ").replace(/u00a0/g," ").replace(/u2424/g,"n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *| *$/g,"").split(/ *| */),align:cap[2].replace(/^ *|| *$/g,"").split(/ *| */),cells:cap[3].replace(/n$/,"").split("n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|d+.) +/,"");if(~item.indexOf("n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("n")+src;i=l-1}}loose=next||/nn(?!s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script"||cap[1]==="style",text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *| *$/g,"").split(/ *| */),align:cap[2].replace(/^ *|| *$/g,"").split(/ *| */),cells:cap[3].replace(/(?: *| *)?n$/,"").split("n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *| *| *| *$/g,"").split(/ *| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^([`*{}[]()#+-.!_>])/,autolink:/^<([^ >]+(@|:/)[^ >]+)>/,url:noop,tag:/^|^</?w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?[(inside)](href)/,reflink:/^!?[(inside)]s*[([^]]*)]/,nolink:/^!?[((?:[[^]]*]|[^[]])*)]/,strong:/^__([sS]+?)__(?!_)|^**([sS]+?)**(?!*)/,em:/^b_((?:__|[sS])+?)_b|^*((?:**|[sS])+?)*(?!*)/,code:/^(`+)s*([sS]*?[^`])s*1(?!`)/,br:/^ {2,}n(?!s*$)/,del:noop,text:/^[sS]+?(?=[<![_*`]| {2,}n|$)/};inline._inside=/(?:[[^]]*]|[^[]]|](?=[^[]*]))*/;inline._href=/s*<?([sS]*?)>?(?:s+['"]([sS]*?)['"])?s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=S)([sS]*?S)__(?!_)|^**(?=S)([sS]*?S)**(?!*)/,em:/^_(?=S)([sS]*?S)_(?!_)|^*(?=S)([sS]*?S)*(?!*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?://[^s<]+[^<.,:;"')]s])/,del:/^~~(?=S)([sS]*?S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(this.smartypants(cap[0]));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"—").replace(/(^|[-u2014/([{"s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-u2014/([{u2018s])"/g,"$1“").replace(/"/g,"”").replace(/.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"
"+(escaped?code:escape(code,true))+"n
"}return'
'+(escaped?code:escape(code,true))+"n
n"};Renderer.prototype.blockquote=function(quote){return"
n"+quote+"
n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^w]+/g,"-")+'">'+text+"</h"+level+">n"};Renderer.prototype.hr=function(){return this.options.xhtml?"
n":"
n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">n"+body+"</"+type+">n"};Renderer.prototype.listitem=function(text){return"
"+text+"
n"};Renderer.prototype.paragraph=function(text){return"
"+text+"
n"};Renderer.prototype.table=function(header,body){return"
n"+"
n"+header+"
n"+"
n"+body+"
n"+"
n"};Renderer.prototype.tablerow=function(content){return"
n"+content+"
n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">n"};Renderer.prototype.strong=function(text){return""+text+""};Renderer.prototype.em=function(text){return""+text+""};Renderer.prototype.codespan=function(text){return""+text+""};Renderer.prototype.br=function(){return this.options.xhtml?"
":"
"};Renderer.prototype.del=function(text){return""+text+""};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0){return""}}var out='"+text+"";return out};Renderer.prototype.image=function(href,title,text){var out='":">";return out};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?w+;)/g:/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^[])^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(){var out,err;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"
An error occured:

"+escape(e.message+"",true)+"
"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}());   No newline at end of file


``` To make an uncontrolled component, `defaultValue` is used instead. <ins> > Note: > > You can pass an array into the `value` attribute, allowing you to select multiple options in a `select` tag: `<select multiple={true} value={['B', 'C']}>`. </ins>


 /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails react-core */ 'use strict'; describe('ReactDOMShorthandCSSPropertyCollision', () => { let React; let ReactDOM; beforeEach(() => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); }); it('should warn for conflicting CSS shorthand updates', () => { const container = document.createElement('div'); ReactDOM.render(
, container); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (fontStyle) ' + 'when a conflicting property is set (font) can lead to styling ' + "bugs. To avoid this, don't mix shorthand and non-shorthand " + 'properties for the same value; instead, replace the shorthand ' + 'with separate values.' + 'n    in div (at **)', ); // These updates are OK and don't warn: ReactDOM.render(
, container); ReactDOM.render(
, container); expect(() => ReactDOM.render(
, container, ), ).toErrorDev( 'Warning: Updating a style property during rerender (font) when ' + 'a conflicting property is set (fontStyle) can lead to styling ' + "bugs. To avoid this, don't mix shorthand and non-shorthand " + 'properties for the same value; instead, replace the shorthand ' + 'with separate values.' + 'n    in div (at **)', ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (font) when ' + 'a conflicting property is set (fontStyle) can lead to styling ' + "bugs. To avoid this, don't mix shorthand and non-shorthand " + 'properties for the same value; instead, replace the shorthand ' + 'with separate values.' + 'n    in div (at **)', ); // A bit of a special case: backgroundPosition isn't technically longhand // (it expands to backgroundPosition{X,Y} but so does background) ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender ' + '(backgroundPosition) when a conflicting property is set ' + "(background) can lead to styling bugs. To avoid this, don't mix " + 'shorthand and non-shorthand properties for the same value; ' + 'instead, replace the shorthand with separate values.' + 'n    in div (at **)', ); ReactDOM.render(
, container, ); // But setting them  at the same time is OK: ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (background) ' + 'when a conflicting property is set (backgroundPosition) can lead ' + "to styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); // A bit of an even more special case: borderLeft and borderStyle overlap. ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (borderStyle) ' + 'when a conflicting property is set (borderLeft) can lead to ' + "styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); expect(() => ReactDOM.render(
, container, ), ).toErrorDev( 'Warning: Updating a style property during rerender (borderStyle) ' + 'when a conflicting property is set (borderLeft) can lead to ' + "styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); // But setting them  at the same time is OK: ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (borderLeft) ' + 'when a conflicting property is set (borderStyle) can lead to ' + "styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); }); });


// Simulate `input.valueAsNumber`. IE9 does not support it var valueAsNumber = parseFloat(node.value, 10) || 0; <del> // eslint-disable-next-line if (value != valueAsNumber) { </del> <ins> if ( // eslint-disable-next-line value != valueAsNumber || // eslint-disable-next-line (value == valueAsNumber && node.value != value) ) { </ins> // Cast `value` to a string to ensure the value is set correctly. While // browsers typically do this as necessary, jsdom doesn't. node.value = '' + value;


let effect = firstEffect; do { <ins> if (__DEV__) { setCurrentFiber(effect); } </ins> if (effect.effectTag & Passive) { let didError = false; let error;


When a user interacts with a React view, the view sends an action (usually represented as a JavaScript object with some fields) through the dispatcher, which notifies the various stores that hold the application's data and business logic. When the stores change state, they notify the views that something has updated. This works especially well with React's declarative model, which allows the stores to send updates without specifying how to transition views between states. <del> Flux is more of a pattern than a formal framework, so you can start using Flux immediately without a lot of new code. An [example of this architecture](https://github.com/facebook/flux/tree/master/examples/flux-todomvc) is available, along with more [detailed documentation](http://facebook.github.io/react/docs/flux-overview.html) and a [tutorial](http://facebook.github.io/react/docs/flux-todo-list.html). Look for more examples to come in the future. </del> <ins> Flux is more of a pattern than a formal framework, so you can start using Flux immediately without a lot of new code. An [example of this architecture](https://github.com/facebook/flux/tree/master/examples/flux-todomvc) is available, along with more [detailed documentation](http://facebook.github.io/flux/docs/overview.html) and a [tutorial](http://facebook.github.io/flux/docs/todo-list.html). Look for more examples to come in the future. </ins>


expect(nodeValueSetter.mock.calls.length).toBe(expected); } <del> if (ReactDOMFeatureFlags.useCreateElement) { renderWithValueAndExpect(undefined, 0); renderWithValueAndExpect('', 1); renderWithValueAndExpect('foo', 2); renderWithValueAndExpect('foo', 2); renderWithValueAndExpect(undefined, 3); renderWithValueAndExpect(null, 3); renderWithValueAndExpect('', 4); renderWithValueAndExpect(undefined, 4); } else { renderWithValueAndExpect(undefined, 0); // This differs because we will have created a node with the value // attribute set. This means it will hasAttribute, so we won't try to // set the value. renderWithValueAndExpect('', 0); renderWithValueAndExpect('foo', 1); renderWithValueAndExpect('foo', 1); renderWithValueAndExpect(undefined, 2); renderWithValueAndExpect(null, 2); // Again, much like the initial update case, we will always have the // attribute set so we won't set the value. renderWithValueAndExpect('', 2); renderWithValueAndExpect(undefined, 2); } </del> <ins> renderWithValueAndExpect(undefined, 0); renderWithValueAndExpect('', 0); renderWithValueAndExpect('foo', 1); renderWithValueAndExpect('foo', 1); renderWithValueAndExpect(undefined, 2); renderWithValueAndExpect(null, 2); renderWithValueAndExpect('', 2); renderWithValueAndExpect(undefined, 2); </ins> }); it('should not incur unnecessary DOM mutations for boolean properties', function() {


injectCode(rendererCode); } <del> // Inject a `__REACT_DEVTOOLS_GLOBAL_HOOK__` global so that React can detect that the // devtools are installed (and skip its suggestion to install the devtools). injectCode( ';(' + installHook.toString() + '(window))' + saveNativeValues + detectReact, ); </del> <ins> // Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with. // Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs. if (document.contentType === 'text/html') { injectCode( ';(' + installHook.toString() + '(window))' + saveNativeValues + detectReact, ); } </ins>


* @param {*} parentType node's parent's type. */ function validateChildKeys(node, parentType) { <ins> if (typeof node !== 'object') { return; } </ins> if (Array.isArray(node)) { for (var i = 0; i < node.length; i++) { var child = node[i];


 {% for item in section.items %}
   {{ item.title }}    {{ item | sidebar_item_link}}  {% if item.subitems %}
 {% for subitem in item.subitems %}
   {{ subitem.title }}    {{ item | sidebar_item_link}}
 {% endfor %}


export default function EditableValue({ className = '', <del> dataType, </del> initialValue, overrideValueFn, path,


}); }, []); <del> return useMemo( () => ({ editableValue, hasPendingChanges: smartStringify(initialValue) !== editableValue, isValid, parsedValue, reset, update, }), [editableValue, initialValue, isValid, parsedValue], ); </del> <ins> return { editableValue, hasPendingChanges: smartStringify(initialValue) !== editableValue, isValid, parsedValue, reset, update, }; </ins> } export function useIsOverflowing(


error => { // Resource failed to load. Stash the error for later so we can throw it // the next time it's requested. <del> const rejectedRecord: RejectedRecord = (pendingRecord: any); </del> <ins> const rejectedRecord: RejectedRecord<K> = (pendingRecord: any); </ins> rejectedRecord.status = Rejected; rejectedRecord.suspender = null; rejectedRecord.error = error;


]); });  it('should shallow render a functional component', function() { function SomeComponent() { return (

 ); } var shallowRenderer = ReactTestUtils.createRenderer(); var result = shallowRenderer.render(<SomeComponent />); expect(result.type).toBe('div'); expect(result.props.children).toEqual([ , , ]); });  it('should throw for invalid elements', function() { var SomeComponent = React.createClass({ render: function() {


 /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails react-core */ 'use strict'; describe('ReactDOMShorthandCSSPropertyCollision', () => { let ReactFeatureFlags; let React; let ReactDOM; beforeEach(() => { jest.resetModules(); ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactFeatureFlags.warnAboutShorthandPropertyCollision = true; React = require('react'); ReactDOM = require('react-dom'); }); it('should warn for conflicting CSS shorthand updates', () => { const container = document.createElement('div'); ReactDOM.render(
, container); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (fontStyle) ' + 'when a conflicting property is set (font) can lead to styling ' + "bugs. To avoid this, don't mix shorthand and non-shorthand " + 'properties for the same value; instead, replace the shorthand ' + 'with separate values.' + 'n    in div (at **)', ); // These updates are OK and don't warn: ReactDOM.render(
, container); ReactDOM.render(
, container); expect(() => ReactDOM.render(
, container, ), ).toErrorDev( 'Warning: Updating a style property during rerender (font) when ' + 'a conflicting property is set (fontStyle) can lead to styling ' + "bugs. To avoid this, don't mix shorthand and non-shorthand " + 'properties for the same value; instead, replace the shorthand ' + 'with separate values.' + 'n    in div (at **)', ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (font) when ' + 'a conflicting property is set (fontStyle) can lead to styling ' + "bugs. To avoid this, don't mix shorthand and non-shorthand " + 'properties for the same value; instead, replace the shorthand ' + 'with separate values.' + 'n    in div (at **)', ); // A bit of a special case: backgroundPosition isn't technically longhand // (it expands to backgroundPosition{X,Y} but so does background) ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender ' + '(backgroundPosition) when a conflicting property is set ' + "(background) can lead to styling bugs. To avoid this, don't mix " + 'shorthand and non-shorthand properties for the same value; ' + 'instead, replace the shorthand with separate values.' + 'n    in div (at **)', ); ReactDOM.render(
, container, ); // But setting them  at the same time is OK: ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (background) ' + 'when a conflicting property is set (backgroundPosition) can lead ' + "to styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); // A bit of an even more special case: borderLeft and borderStyle overlap. ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (borderStyle) ' + 'when a conflicting property is set (borderLeft) can lead to ' + "styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); expect(() => ReactDOM.render(
, container, ), ).toErrorDev( 'Warning: Updating a style property during rerender (borderStyle) ' + 'when a conflicting property is set (borderLeft) can lead to ' + "styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); // But setting them  at the same time is OK: ReactDOM.render(
, container, ); expect(() => ReactDOM.render(
, container), ).toErrorDev( 'Warning: Removing a style property during rerender (borderLeft) ' + 'when a conflicting property is set (borderStyle) can lead to ' + "styling bugs. To avoid this, don't mix shorthand and " + 'non-shorthand properties for the same value; instead, replace the ' + 'shorthand with separate values.' + 'n    in div (at **)', ); }); });


var node = ReactDOM.findDOMNode(stub); expect(node.innerHTML).toBe('foobar'); }); <ins> it('should set attribute for empty value', function() { var container = document.createElement('div'); var option = ReactDOM.render(<option value="" />, container); expect(option.hasAttribute('value')).toBe(true); expect(option.getAttribute('value')).toBe(''); ReactDOM.render(<option value="lava" />, container); expect(option.hasAttribute('value')).toBe(true); expect(option.getAttribute('value')).toBe('lava'); }); </ins> });


miss: A => Promise<V>, missArg: A, ): V { <del> const record: Record<V> = accessRecord(resourceType, key); </del> <ins> const record: Record<K, V> = accessRecord(resourceType, key); </ins> switch (record.status) { case Empty: // Load the requested resource.


key: K, value: V, error: null, <del> next: Record<K, V> | null, previous: Record<K, V> | null, </del> <ins> next: any, // TODO: (issue #12941) previous: any, // TODO: (issue #12941) /** * Proper types would be something like this: * next: Record<K, V> | null, * previous: Record<K, V> | null, */ </ins> |}; <del> type RejectedRecord<K, V> = {| </del> <ins> type RejectedRecord<K> = {| </ins> status: 3, suspender: null, key: K, value: null, error: Error, <del> next: Record<K, V> | null, previous: Record<K, V> | null, </del> <ins> next: any, // TODO: (issue #12941) previous: any, // TODO: (issue #12941) /** * Proper types would be something like this: * next: Record<K, V> | null, * previous: Record<K, V> | null, */ </ins> |}; type Record<K, V> = <del> | EmptyRecord<K, V> </del> <ins> | EmptyRecord<K> </ins> | PendingRecord<K, V> | ResolvedRecord<K, V> <del> | RejectedRecord<K, V>; </del> <ins> | RejectedRecord<K>; </ins> type RecordCache<K, V> = {| map: Map<K, Record<K, V>>, head: Record<K, V> | null, tail: Record<K, V> | null, <ins> size: number, </ins> |}; // TODO: How do you express this type with Flow?


 {this.props.author}
  {converter.makeHtml(this.props.children.toString())}   {marked(this.props.children.toString())}
 ); } }); ``` <del> All we're doing here is calling the Showdown library. We need to convert `this.props.children` from React's wrapped text to a raw string that Showdown will understand so we explicitly call `toString()`. </del> <ins> All we're doing here is calling the marked library. We need to convert `this.props.children` from React's wrapped text to a raw string that marked will understand so we explicitly call `toString()`. </ins> But there's a problem! Our rendered comments look like this in the browser: "`<p>`This is `<em>`another`</em>` comment`</p>`". We want those tags to actually render as HTML. That's React protecting you from an XSS attack. There's a way to get around it but the framework warns you not to use it: <del> ```javascript{5,11}   ```javascript{4,10}  // tutorial7.js  var converter = new Showdown.converter();  var Comment = React.createClass({ render: function() {  var rawMarkup = converter.makeHtml(this.props.children.toString());   var rawMarkup = marked(this.props.children.toString(), {sanitize: true});  return (


--- markdown: redcarpet name: React <ins> description: A JavaScript library for building user interfaces </ins> redcarpet: extensions: - fenced_code_blocks