SlideShare a Scribd company logo
1 of 49
Download to read offline
Simplifying JavaScript Projects
with ReactJS and Friends
Kevin Dangoor, Sr. Computer Scientist, Adobe
1DevDay Detroit 2014
A modern, open source text editor that understands web design.
Brackets
• MIT-licensed open source
• Sponsored by Adobe with hundreds of contributors and a number of
non-Adobe committers
• 13th most starred project
• Hundreds of extensions are available
• 1.0 just released 10 days ago, with Extract for Brackets
function	
  _documentSelectionFocusChange()	
  {	
  
	
  	
  	
  	
  var	
  curFile	
  =	
  EditorManager.getCurrentlyViewedPath();	
  
	
  	
  	
  	
  if	
  (curFile	
  &&	
  _hasFileSelectionFocus())	
  {	
  
function	
  _documentSelectionFocusChange()	
  {	
  
	
  	
  	
  	
  var	
  curFile	
  =	
  EditorManager.getCurrentlyViewedPath();	
  
	
  	
  	
  	
  if	
  (curFile	
  &&	
  _hasFileSelectionFocus())	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  nodeFound	
  =	
  $("#project-­‐files-­‐container	
  li").is(function	
  (index)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  $treeNode	
  =	
  $(this),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  entry	
  =	
  $treeNode.data("entry");	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (entry	
  &&	
  entry.fullPath	
  ===	
  curFile)	
  {	
  
	
  
function	
  _documentSelectionFocusChange()	
  {	
  
	
  	
  	
  	
  var	
  curFile	
  =	
  EditorManager.getCurrentlyViewedPath();	
  
	
  	
  	
  	
  if	
  (curFile	
  &&	
  _hasFileSelectionFocus())	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  nodeFound	
  =	
  $("#project-­‐files-­‐container	
  li").is(function	
  (index)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  $treeNode	
  =	
  $(this),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  entry	
  =	
  $treeNode.data("entry");	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (entry	
  &&	
  entry.fullPath	
  ===	
  curFile)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (!_projectTree.jstree("is_selected",	
  $treeNode))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  ($treeNode.parents(".jstree-­‐closed").length)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //don't	
  auto-­‐expand	
  tree	
  to	
  show	
  file	
  -­‐	
  but	
  remember	
  it	
  if	
  parent	
  is	
  manually	
  
expanded	
  later	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _projectTree.jstree("deselect_all");	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _lastSelected	
  =	
  $treeNode;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  
if	
  (!_projectTree.jstree("is_selected",	
  $treeNode))	
  {	
  
	
  	
  	
  	
  if	
  ($treeNode.parents(".jstree-­‐closed").length)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  //don't	
  auto-­‐expand	
  tree	
  to	
  show	
  file	
  -­‐	
  but	
  remember	
  it	
  if	
  parent	
  is	
  manually	
  expanded	
  later	
  
	
  	
  	
  	
  	
  	
  	
  	
  _projectTree.jstree("deselect_all");	
  
	
  	
  	
  	
  	
  	
  	
  	
  _lastSelected	
  =	
  $treeNode;	
  
	
  	
  	
  	
  }	
  else	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  //we	
  don't	
  want	
  to	
  trigger	
  another	
  selection	
  change	
  event,	
  so	
  manually	
  deselect	
  
	
  	
  	
  	
  	
  	
  	
  	
  //and	
  select	
  without	
  sending	
  out	
  notifications	
  
	
  	
  	
  	
  	
  	
  	
  	
  _projectTree.jstree("deselect_all");	
  
	
  	
  	
  	
  	
  	
  	
  	
  _projectTree.jstree("select_node",	
  $treeNode,	
  false);	
  //	
  sets	
  _lastSelected	
  
	
  	
  	
  	
  }	
  
}	
  
	
  
If it’s hard to test,
it won’t be tested.
http://www.infoq.com/presentations/Simple-Made-Easy
simple!
composed of one thing, not combined
complect!
interweave
function	
  _documentSelectionFocusChange()	
  {	
  
	
  	
  	
  	
  var	
  curFile	
  =	
  EditorManager.getCurrentlyViewedPath();	
  
	
  	
  	
  	
  if	
  (curFile	
  &&	
  _hasFileSelectionFocus())	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  nodeFound	
  =	
  $("#project-­‐files-­‐container	
  li").is(function	
  (index)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  $treeNode	
  =	
  $(this),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  entry	
  =	
  $treeNode.data("entry");	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (entry	
  &&	
  entry.fullPath	
  ===	
  curFile)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (!_projectTree.jstree("is_selected",	
  $treeNode))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  ($treeNode.parents(".jstree-­‐closed").length)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //don't	
  auto-­‐expand	
  tree	
  to	
  show	
  file	
  -­‐	
  but	
  remember	
  it	
  if	
  parent	
  is	
  manually	
  
expanded	
  later	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _projectTree.jstree("deselect_all");	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _lastSelected	
  =	
  $treeNode;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  
Simple?
easy!
near at hand
React
https://www.destroyallsoftware.com/talks/boundaries
Functional core,
Integration shell
function	
  _documentSelectionFocusChange()	
  {	
  
	
  	
  	
  	
  var	
  curFile	
  =	
  EditorManager.getCurrentlyViewedPath();	
  
	
  	
  	
  	
  if	
  (curFile	
  &&	
  _hasFileSelectionFocus())	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  nodeFound	
  =	
  $("#project-­‐files-­‐container	
  li").is(function	
  (index)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  $treeNode	
  =	
  $(this),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  entry	
  =	
  $treeNode.data("entry");	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (entry	
  &&	
  entry.fullPath	
  ===	
  curFile)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (!_projectTree.jstree("is_selected",	
  $treeNode))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  ($treeNode.parents(".jstree-­‐closed").length)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //don't	
  auto-­‐expand	
  tree	
  to	
  show	
  file	
  -­‐	
  but	
  remember	
  it	
  if	
  parent	
  is	
  manually	
  
expanded	
  later	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _projectTree.jstree("deselect_all");	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  _lastSelected	
  =	
  $treeNode;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  
 	
  	
  	
  function	
  _documentSelectionFocusChange()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  curFullPath	
  =	
  MainViewManager.getCurrentlyViewedPath(MainViewManager.ACTIVE_PANE);	
  
	
  	
  	
  	
  	
  	
  	
  	
  if	
  (curFullPath	
  &&	
  _hasFileSelectionFocus())	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  actionCreator.setSelected(curFullPath,	
  true);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  actionCreator.setSelected(null);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  _fileViewControllerChange();	
  
	
  	
  	
  	
  }	
  
	
  
http://futurice.com/blog/reactive-mvc-and-the-virtual-dom/
ProjectModel
FileTreeViewModel
FileTreeView ActionCreator
React
 	
  	
  	
  function	
  render(element,	
  viewModel,	
  projectRoot,	
  actions,	
  forceRender,	
  platform)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  if	
  (!projectRoot)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return;	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  React.renderComponent(fileTreeView({	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  treeData:	
  viewModel.treeData,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  selectionViewInfo:	
  viewModel.selectionViewInfo,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  sortDirectoriesFirst:	
  viewModel.sortDirectoriesFirst,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  parentPath:	
  projectRoot.fullPath,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  actions:	
  actions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  extensions:	
  _extensions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  platform:	
  platform,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  forceRender:	
  forceRender	
  
	
  	
  	
  	
  	
  	
  	
  	
  }),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  element);	
  
	
  	
  	
  	
  }
 	
  	
  	
  var	
  fileTreeView	
  =	
  React.createClass({	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  /**	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  *	
  Update	
  for	
  any	
  change	
  in	
  the	
  tree	
  data	
  or	
  directory	
  sorting	
  preference.	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  */	
  
	
  	
  	
  	
  	
  	
  	
  	
  shouldComponentUpdate:	
  function	
  (nextProps,	
  nextState)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  nextProps.forceRender	
  ||	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.props.treeData	
  !==	
  nextProps.treeData	
  ||	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.props.sortDirectoriesFirst	
  !==	
  nextProps.sortDirectoriesFirst	
  ||	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.props.extensions	
  !==	
  nextProps.extensions	
  ||	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.props.selectionViewInfo	
  !==	
  nextProps.selectionViewInfo;	
  
	
  	
  	
  	
  	
  	
  	
  	
  },
 	
  	
  	
  	
  	
  	
  	
  render:	
  function	
  ()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  contents	
  =	
  directoryContents({	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  isRoot:	
  true,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  parentPath:	
  this.props.parentPath,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  sortDirectoriesFirst:	
  this.props.sortDirectoriesFirst,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  contents:	
  this.props.treeData,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  extensions:	
  this.props.extensions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  actions:	
  this.props.actions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  forceRender:	
  this.props.forceRender,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  platform:	
  this.props.platform	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  });	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  DOM.div(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  null,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  selectionBackground,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  contextBackground,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  extensionForSelection,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  extensionForContext,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  contents	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  );	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  });
 	
  	
  	
  directoryContents	
  =	
  React.createClass({	
  
	
  	
  	
  	
  	
  	
  	
  	
  render:	
  function	
  ()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  extensions	
  =	
  this.props.extensions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  iconClass	
  =	
  extensions	
  &&	
  extensions.get("icons")	
  ?	
  "jstree-­‐icons"	
  :	
  "jstree-­‐no-­‐
icons",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ulProps	
  =	
  this.props.isRoot	
  ?	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  className:	
  "jstree-­‐brackets	
  jstree-­‐no-­‐dots	
  "	
  +	
  iconClass	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  :	
  null;	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  contents	
  =	
  this.props.contents,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  namesInOrder	
  =	
  _sortDirectoryContents(contents,	
  this.props.sortDirectoriesFirst);
 	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  DOM.ul(ulProps,	
  namesInOrder.map(function	
  (name)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  entry	
  =	
  contents.get(name);	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (FileTreeViewModel.isFile(entry))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  fileNode({	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  parentPath:	
  this.props.parentPath,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  name:	
  name,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  entry:	
  entry,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  actions:	
  this.props.actions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  extensions:	
  this.props.extensions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  forceRender:	
  this.props.forceRender,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  platform:	
  this.props.platform,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  key:	
  name	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  });	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  directoryNode({
 	
  	
  	
  directoryNode	
  =	
  React.createClass({	
  
	
  	
  	
  	
  	
  	
  	
  	
  mixins:	
  [contextSettable,	
  pathComputer,	
  extendable],	
  
	
  	
  	
  	
  	
  	
  	
  	
  render:	
  function	
  ()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  entry	
  =	
  this.props.entry,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (entry.get("rename"))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  renameInput	
  =	
  directoryRenameInput({	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  actions:	
  this.props.actions,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  entry:	
  this.props.entry,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  name:	
  this.props.name,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  parentPath:	
  this.props.parentPath	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  });	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }
 	
  	
  	
  directoryNode	
  =	
  React.createClass({	
  
	
  	
  	
  	
  	
  	
  	
  	
  render:	
  function	
  ()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  DOM.li({	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  className:	
  this.getClasses("jstree-­‐"	
  +	
  nodeClass),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  onClick:	
  this.handleClick,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  onMouseDown:	
  this.handleMouseDown	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  },	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  DOM.ins({	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  className:	
  "jstree-­‐icon"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  },	
  "	
  "),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  renameInput,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  nameDisplay,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  childNodes);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  });
 	
  	
  	
  	
  	
  	
  	
  handleClick:	
  function	
  (event)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  isOpen	
  =	
  this.props.entry.get("open"),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  setOpen	
  =	
  isOpen	
  ?	
  false	
  :	
  true;	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (event.metaKey	
  ||	
  event.ctrlKey)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  ctrl-­‐alt-­‐click	
  toggles	
  this	
  directory	
  and	
  its	
  children	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (event.altKey)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (setOpen)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  when	
  opening,	
  we	
  only	
  open	
  the	
  immediate	
  children	
  because	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  opening	
  a	
  whole	
  subtree	
  could	
  be	
  really	
  slow	
  (consider	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  a	
  `node_modules`	
  directory,	
  for	
  example).	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.props.actions.toggleSubdirectories(this.myPath(),	
  setOpen);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.props.actions.setDirectoryOpen(this.myPath(),	
  setOpen);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  When	
  closing,	
  we	
  recursively	
  close	
  the	
  whole	
  subtree.	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.props.actions.closeSubtree(this.myPath());	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {
 	
  	
  	
  ActionCreator.prototype.toggleSubdirectories	
  =	
  function	
  (path,	
  openOrClose)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  this.model.toggleSubdirectories(path,	
  openOrClose).then(_saveTreeState);	
  
	
  	
  	
  	
  };
 	
  	
  	
  ProjectModel.prototype.toggleSubdirectories	
  =	
  function	
  (path,	
  openOrClose)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  self	
  =	
  this,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  d	
  =	
  new	
  $.Deferred();	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  this.setDirectoryOpen(path,	
  true).then(function	
  ()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  projectRelativePath	
  =	
  self.makeProjectRelativeIfPossible(path),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  childNodes	
  =	
  self._viewModel.getChildDirectories(projectRelativePath);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Async.doInParallel(childNodes,	
  function	
  (node)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  self.setDirectoryOpen(path	
  +	
  node,	
  openOrClose);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  },	
  true).then(function	
  ()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  d.resolve();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  },	
  function	
  (err)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  d.reject(err);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  });	
  
	
  	
  	
  	
  	
  	
  	
  	
  });	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  return	
  d.promise();	
  
	
  	
  	
  	
  };
 	
  	
  	
  ProjectModel.prototype.setDirectoryOpen	
  =	
  function	
  (path,	
  open)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  projectRelative	
  =	
  this.makeProjectRelativeIfPossible(path),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  needsLoading	
  	
  	
  	
  =	
  !this._viewModel.isPathLoaded(projectRelative),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  d	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  =	
  new	
  $.Deferred(),	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  self	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  =	
  this;	
  
	
  	
  	
  	
  	
  	
  	
  	
  if	
  (open	
  &&	
  needsLoading)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  parentDirectory	
  =	
  FileUtils.getDirectoryPath(FileUtils.stripTrailingSlash(path));	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.setDirectoryOpen(parentDirectory,	
  true).then(function	
  ()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  self._getDirectoryContents(path).then(onSuccess).fail(function	
  (err)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  d.reject(err);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  });	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  },	
  function	
  (err)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  d.reject(err);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  });	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  onSuccess();	
  
	
  	
  	
  	
  	
  	
  	
  	
  }
 	
  	
  	
  	
  	
  	
  	
  function	
  onSuccess(contents)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  Update	
  the	
  view	
  model	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (contents)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  self._viewModel.setDirectoryContents(projectRelative,	
  contents);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (open)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  self._viewModel.openPath(projectRelative);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (self._focused)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  currentPathInProject	
  =	
  self.makeProjectRelativeIfPossible(self._currentPath);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (self._viewModel.isFilePathVisible(currentPathInProject))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  self.setSelected(self._currentPath,	
  true);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  self.setSelected(null);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {
branches
to test
 	
  	
  	
  FileTreeViewModel.prototype.openPath	
  =	
  function	
  (path)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  this._commit(_openPath(this._treeData,	
  path));	
  
	
  	
  	
  	
  };
{	
  
	
  	
  	
  	
  "subdir":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  open:	
  true,	
  
	
  	
  	
  	
  	
  	
  	
  	
  children:	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "afile.js":	
  {},	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "subsubdir":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  children:	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "thirdsub":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  children:	
  {}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}
subdir/subsubdir/thirdsub/
{	
  
	
  	
  	
  	
  "subdir":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  open:	
  true,	
  
	
  	
  	
  	
  	
  	
  	
  	
  children:	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "afile.js":	
  {},	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "subsubdir":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  children:	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "thirdsub":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  children:	
  {}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}
subdir/subsubdir/thirdsub/
treeData.subdir.subsubdir.open	
  =	
  true;	
  
treeData.subdir.subsubdir.thirdsub.open	
  =	
  true;
thirdsub	
  =	
  treeData.subdir.subsubdir.thirdsub;	
  
newThirdSub	
  =	
  thirdsub.set("open",	
  true);	
  
	
  
thirdsub	
  !==	
  newThirdSub;	
  //	
  true	
  
treeData.subdir.subsubdir.thirdsub	
  !==	
  newThirdSub	
  //	
  true	
  
treeData.subdir.subsubdir.thirdsub.open	
  ===	
  undefined;	
  //	
  true
Attack of the clones
https://www.flickr.com/photos/hjmediastudios/7910348016/
thirdsub	
  =	
  treeData.subdir.subsubdir.thirdsub;	
  
newThirdSub	
  =	
  thirdsub.set("open",	
  true);	
  
	
  
thirdsub	
  !==	
  newThirdSub;	
  //	
  true	
  
treeData.subdir.subsubdir.thirdsub	
  !==	
  newThirdSub	
  //	
  true	
  
treeData.subdir.subsubdir.thirdsub.open	
  ===	
  undefined;	
  //	
  true
thirdsub	
  =	
  treeData.subdir.subsubdir.thirdsub;	
  
newThirdSub	
  =	
  thirdsub.set("open",	
  true);	
  
newSubSubDir	
  =	
  subsubdir.set("thirdsub",	
  newThirdSub);	
  
newSubDir	
  =	
  subdir.set("subsubdir",	
  newSubSubDir);	
  
treeData	
  =	
  treeData.set("subdir",	
  newSubDir);
 	
  	
  	
  function	
  _openPath(treeData,	
  path)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  objectPath	
  =	
  _filePathToObjectPath(treeData,	
  path);	
  
	
  	
  	
  	
  	
  	
  	
  	
  function	
  setOpen(node)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  node.set("open",	
  true);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  while	
  (objectPath	
  &&	
  objectPath.length)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  node	
  =	
  treeData.getIn(objectPath);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (isFile(node))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  objectPath.pop();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (!node.get("open"))	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  treeData	
  =	
  treeData.updateIn(objectPath,	
  setOpen);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  objectPath.pop();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (objectPath.length)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  objectPath.pop();	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  return	
  treeData;	
  
	
  	
  	
  	
  }
mutable?
 	
  	
  	
  FileTreeViewModel.prototype.openPath	
  =	
  function	
  (path)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  this._commit(_openPath(this._treeData,	
  path));	
  
	
  	
  	
  	
  };
 	
  	
  	
  FileTreeViewModel.prototype._commit	
  =	
  function	
  (treeData,	
  selectionViewInfo)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  var	
  changed	
  =	
  false;	
  
	
  	
  	
  	
  	
  	
  	
  	
  if	
  (treeData	
  &&	
  treeData	
  !==	
  this._treeData)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this._treeData	
  =	
  treeData;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  changed	
  =	
  true;	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  if	
  (selectionViewInfo	
  &&	
  selectionViewInfo	
  !==	
  this._selectionViewInfo)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this._selectionViewInfo	
  =	
  selectionViewInfo;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  changed	
  =	
  true;	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  if	
  (changed)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  $(this).trigger(EVENT_CHANGE);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  };
–C.A.R. Hoare
“There are two ways of constructing a software design: One way
is to make it so simple that there are obviously no
deficiencies, and the other way is to make it so complicated
that there are no obvious deficiencies. The first method is far
more difficult.”
Simple?
simple!
composed of one thing, not combined
http://www.shaffner.us/cs/papers/tarpit.pdf
Simple Architecture
• Each part does one thing with side effects in few, known places
• React lets you generate a whole UI functionally
• Immutable-JS allows you to control when data updates occur
• Every part of your application can have a consistent state
• Object identity tells you when something has changed
A modern, open source text editor that understands web design.
http://brackets.io

More Related Content

What's hot

Drupal Javascript for developers
Drupal Javascript for developersDrupal Javascript for developers
Drupal Javascript for developersDream Production AG
 
Magento Dependency Injection
Magento Dependency InjectionMagento Dependency Injection
Magento Dependency InjectionAnton Kril
 
Introduction to Web Components
Introduction to Web ComponentsIntroduction to Web Components
Introduction to Web ComponentsFelix Arntz
 
Drupal & javascript
Drupal & javascriptDrupal & javascript
Drupal & javascriptAlmog Baku
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
Beyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance JavascriptBeyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance Javascriptaglemann
 
Upgrade your javascript to drupal 8
Upgrade your javascript to drupal 8Upgrade your javascript to drupal 8
Upgrade your javascript to drupal 8Théodore Biadala
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code OrganizationRebecca Murphey
 
Render API - Pavel Makhrinsky
Render API - Pavel MakhrinskyRender API - Pavel Makhrinsky
Render API - Pavel MakhrinskyDrupalCampDN
 
Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Luka Zakrajšek
 
AngularJS - $http & $resource Services
AngularJS - $http & $resource ServicesAngularJS - $http & $resource Services
AngularJS - $http & $resource ServicesEyal Vardi
 
HirshHorn theme: how I created it
HirshHorn theme: how I created itHirshHorn theme: how I created it
HirshHorn theme: how I created itPaul Bearne
 
Geodaten & Drupal 7
Geodaten & Drupal 7Geodaten & Drupal 7
Geodaten & Drupal 7Michael Milz
 
AngularJS Compile Process
AngularJS Compile ProcessAngularJS Compile Process
AngularJS Compile ProcessEyal Vardi
 
Opencast Admin UI - Introduction to developing using AngularJS
Opencast Admin UI - Introduction to developing using AngularJSOpencast Admin UI - Introduction to developing using AngularJS
Opencast Admin UI - Introduction to developing using AngularJSbuttyx
 

What's hot (19)

Drupal Javascript for developers
Drupal Javascript for developersDrupal Javascript for developers
Drupal Javascript for developers
 
Magento Dependency Injection
Magento Dependency InjectionMagento Dependency Injection
Magento Dependency Injection
 
Java Assignment Help
Java  Assignment  HelpJava  Assignment  Help
Java Assignment Help
 
Introduction to Web Components
Introduction to Web ComponentsIntroduction to Web Components
Introduction to Web Components
 
Drupal & javascript
Drupal & javascriptDrupal & javascript
Drupal & javascript
 
Hibernate
HibernateHibernate
Hibernate
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Beyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance JavascriptBeyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance Javascript
 
Upgrade your javascript to drupal 8
Upgrade your javascript to drupal 8Upgrade your javascript to drupal 8
Upgrade your javascript to drupal 8
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
 
Render API - Pavel Makhrinsky
Render API - Pavel MakhrinskyRender API - Pavel Makhrinsky
Render API - Pavel Makhrinsky
 
Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
 
AngularJS - $http & $resource Services
AngularJS - $http & $resource ServicesAngularJS - $http & $resource Services
AngularJS - $http & $resource Services
 
jQuery Presentasion
jQuery PresentasionjQuery Presentasion
jQuery Presentasion
 
HirshHorn theme: how I created it
HirshHorn theme: how I created itHirshHorn theme: how I created it
HirshHorn theme: how I created it
 
Drupal 8: Fields reborn
Drupal 8: Fields rebornDrupal 8: Fields reborn
Drupal 8: Fields reborn
 
Geodaten & Drupal 7
Geodaten & Drupal 7Geodaten & Drupal 7
Geodaten & Drupal 7
 
AngularJS Compile Process
AngularJS Compile ProcessAngularJS Compile Process
AngularJS Compile Process
 
Opencast Admin UI - Introduction to developing using AngularJS
Opencast Admin UI - Introduction to developing using AngularJSOpencast Admin UI - Introduction to developing using AngularJS
Opencast Admin UI - Introduction to developing using AngularJS
 

Viewers also liked

React Next Conference slides: ReactJS Worst practices
React Next Conference slides: ReactJS Worst practicesReact Next Conference slides: ReactJS Worst practices
React Next Conference slides: ReactJS Worst practicesKateryna Porshnieva
 
Starting with Reactjs
Starting with ReactjsStarting with Reactjs
Starting with ReactjsThinh VoXuan
 
React.js in real world apps.
React.js in real world apps. React.js in real world apps.
React.js in real world apps. Emanuele DelBono
 
An Introduction to ReactJS
An Introduction to ReactJSAn Introduction to ReactJS
An Introduction to ReactJSAll Things Open
 
React in Native Apps - Meetup React - 20150409
React in Native Apps - Meetup React - 20150409React in Native Apps - Meetup React - 20150409
React in Native Apps - Meetup React - 20150409Minko3D
 
Rethinking Best Practices
Rethinking Best PracticesRethinking Best Practices
Rethinking Best Practicesfloydophone
 
React JS and why it's awesome
React JS and why it's awesomeReact JS and why it's awesome
React JS and why it's awesomeAndrew Hull
 

Viewers also liked (11)

Intro to ReactJS
Intro to ReactJSIntro to ReactJS
Intro to ReactJS
 
React Webinar Slides
React Webinar SlidesReact Webinar Slides
React Webinar Slides
 
React Next Conference slides: ReactJS Worst practices
React Next Conference slides: ReactJS Worst practicesReact Next Conference slides: ReactJS Worst practices
React Next Conference slides: ReactJS Worst practices
 
Starting with Reactjs
Starting with ReactjsStarting with Reactjs
Starting with Reactjs
 
React.js in real world apps.
React.js in real world apps. React.js in real world apps.
React.js in real world apps.
 
An Introduction to ReactJS
An Introduction to ReactJSAn Introduction to ReactJS
An Introduction to ReactJS
 
React js
React jsReact js
React js
 
React in Native Apps - Meetup React - 20150409
React in Native Apps - Meetup React - 20150409React in Native Apps - Meetup React - 20150409
React in Native Apps - Meetup React - 20150409
 
Why Play Framework is fast
Why Play Framework is fastWhy Play Framework is fast
Why Play Framework is fast
 
Rethinking Best Practices
Rethinking Best PracticesRethinking Best Practices
Rethinking Best Practices
 
React JS and why it's awesome
React JS and why it's awesomeReact JS and why it's awesome
React JS and why it's awesome
 

Similar to Simplifying JavaScript Projects with ReactJS

JQuery In Drupal
JQuery In DrupalJQuery In Drupal
JQuery In Drupalkatbailey
 
8 things to know about theming in drupal 8
8 things to know about theming in drupal 88 things to know about theming in drupal 8
8 things to know about theming in drupal 8Logan Farr
 
BlackBerry DevCon 2011 - PhoneGap and WebWorks
BlackBerry DevCon 2011 - PhoneGap and WebWorksBlackBerry DevCon 2011 - PhoneGap and WebWorks
BlackBerry DevCon 2011 - PhoneGap and WebWorksmwbrooks
 
Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...
Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...
Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...DicodingEvent
 
Doctrine 2
Doctrine 2Doctrine 2
Doctrine 2zfconfua
 
Zend Framework 2 - Basic Components
Zend Framework 2  - Basic ComponentsZend Framework 2  - Basic Components
Zend Framework 2 - Basic ComponentsMateusz Tymek
 
jQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformancejQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformanceJonas De Smet
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery ApplicationsRebecca Murphey
 
J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012ghnash
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
jQuery: out with the old, in with the new
jQuery: out with the old, in with the newjQuery: out with the old, in with the new
jQuery: out with the old, in with the newRemy Sharp
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmersAlexander Varwijk
 
Drupal II: The SQL
Drupal II: The SQLDrupal II: The SQL
Drupal II: The SQLddiers
 
How to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQueryHow to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQuerykolkatageeks
 
Taming that client side mess with Backbone.js
Taming that client side mess with Backbone.jsTaming that client side mess with Backbone.js
Taming that client side mess with Backbone.jsJarod Ferguson
 

Similar to Simplifying JavaScript Projects with ReactJS (20)

JQuery In Drupal
JQuery In DrupalJQuery In Drupal
JQuery In Drupal
 
8 things to know about theming in drupal 8
8 things to know about theming in drupal 88 things to know about theming in drupal 8
8 things to know about theming in drupal 8
 
Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
BlackBerry DevCon 2011 - PhoneGap and WebWorks
BlackBerry DevCon 2011 - PhoneGap and WebWorksBlackBerry DevCon 2011 - PhoneGap and WebWorks
BlackBerry DevCon 2011 - PhoneGap and WebWorks
 
Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...
Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...
Dicoding Developer Coaching #27: Android | Membuat Aplikasi Support Online Ma...
 
Doctrine 2
Doctrine 2Doctrine 2
Doctrine 2
 
Zend Framework 2 - Basic Components
Zend Framework 2  - Basic ComponentsZend Framework 2  - Basic Components
Zend Framework 2 - Basic Components
 
jQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformancejQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and Performance
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
 
J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
jQuery: out with the old, in with the new
jQuery: out with the old, in with the newjQuery: out with the old, in with the new
jQuery: out with the old, in with the new
 
Introducing jQuery
Introducing jQueryIntroducing jQuery
Introducing jQuery
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
 
Drupal II: The SQL
Drupal II: The SQLDrupal II: The SQL
Drupal II: The SQL
 
How to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQueryHow to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQuery
 
Taming that client side mess with Backbone.js
Taming that client side mess with Backbone.jsTaming that client side mess with Backbone.js
Taming that client side mess with Backbone.js
 

Recently uploaded

Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...SelfMade bd
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationJuha-Pekka Tolvanen
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benonimasabamasaba
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfonteinmasabamasaba
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024VictoriaMetrics
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...masabamasaba
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...masabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...masabamasaba
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareJim McKeeth
 

Recently uploaded (20)

Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 

Simplifying JavaScript Projects with ReactJS

  • 1. Simplifying JavaScript Projects with ReactJS and Friends Kevin Dangoor, Sr. Computer Scientist, Adobe 1DevDay Detroit 2014
  • 2. A modern, open source text editor that understands web design.
  • 3. Brackets • MIT-licensed open source • Sponsored by Adobe with hundreds of contributors and a number of non-Adobe committers • 13th most starred project • Hundreds of extensions are available • 1.0 just released 10 days ago, with Extract for Brackets
  • 4.
  • 5. function  _documentSelectionFocusChange()  {          var  curFile  =  EditorManager.getCurrentlyViewedPath();          if  (curFile  &&  _hasFileSelectionFocus())  {  
  • 6. function  _documentSelectionFocusChange()  {          var  curFile  =  EditorManager.getCurrentlyViewedPath();          if  (curFile  &&  _hasFileSelectionFocus())  {                  var  nodeFound  =  $("#project-­‐files-­‐container  li").is(function  (index)  {                          var  $treeNode  =  $(this),                                  entry  =  $treeNode.data("entry");                          if  (entry  &&  entry.fullPath  ===  curFile)  {    
  • 7. function  _documentSelectionFocusChange()  {          var  curFile  =  EditorManager.getCurrentlyViewedPath();          if  (curFile  &&  _hasFileSelectionFocus())  {                  var  nodeFound  =  $("#project-­‐files-­‐container  li").is(function  (index)  {                          var  $treeNode  =  $(this),                                  entry  =  $treeNode.data("entry");                          if  (entry  &&  entry.fullPath  ===  curFile)  {                                  if  (!_projectTree.jstree("is_selected",  $treeNode))  {                                          if  ($treeNode.parents(".jstree-­‐closed").length)  {                                                  //don't  auto-­‐expand  tree  to  show  file  -­‐  but  remember  it  if  parent  is  manually   expanded  later                                                  _projectTree.jstree("deselect_all");                                                  _lastSelected  =  $treeNode;                                          }  else  {    
  • 8. if  (!_projectTree.jstree("is_selected",  $treeNode))  {          if  ($treeNode.parents(".jstree-­‐closed").length)  {                  //don't  auto-­‐expand  tree  to  show  file  -­‐  but  remember  it  if  parent  is  manually  expanded  later                  _projectTree.jstree("deselect_all");                  _lastSelected  =  $treeNode;          }  else  {                  //we  don't  want  to  trigger  another  selection  change  event,  so  manually  deselect                  //and  select  without  sending  out  notifications                  _projectTree.jstree("deselect_all");                  _projectTree.jstree("select_node",  $treeNode,  false);  //  sets  _lastSelected          }   }    
  • 9. If it’s hard to test, it won’t be tested.
  • 11. simple! composed of one thing, not combined complect! interweave
  • 12. function  _documentSelectionFocusChange()  {          var  curFile  =  EditorManager.getCurrentlyViewedPath();          if  (curFile  &&  _hasFileSelectionFocus())  {                  var  nodeFound  =  $("#project-­‐files-­‐container  li").is(function  (index)  {                          var  $treeNode  =  $(this),                                  entry  =  $treeNode.data("entry");                          if  (entry  &&  entry.fullPath  ===  curFile)  {                                  if  (!_projectTree.jstree("is_selected",  $treeNode))  {                                          if  ($treeNode.parents(".jstree-­‐closed").length)  {                                                  //don't  auto-­‐expand  tree  to  show  file  -­‐  but  remember  it  if  parent  is  manually   expanded  later                                                  _projectTree.jstree("deselect_all");                                                  _lastSelected  =  $treeNode;                                          }  else  {     Simple?
  • 14. React
  • 17. function  _documentSelectionFocusChange()  {          var  curFile  =  EditorManager.getCurrentlyViewedPath();          if  (curFile  &&  _hasFileSelectionFocus())  {                  var  nodeFound  =  $("#project-­‐files-­‐container  li").is(function  (index)  {                          var  $treeNode  =  $(this),                                  entry  =  $treeNode.data("entry");                          if  (entry  &&  entry.fullPath  ===  curFile)  {                                  if  (!_projectTree.jstree("is_selected",  $treeNode))  {                                          if  ($treeNode.parents(".jstree-­‐closed").length)  {                                                  //don't  auto-­‐expand  tree  to  show  file  -­‐  but  remember  it  if  parent  is  manually   expanded  later                                                  _projectTree.jstree("deselect_all");                                                  _lastSelected  =  $treeNode;                                          }  else  {    
  • 18.        function  _documentSelectionFocusChange()  {                  var  curFullPath  =  MainViewManager.getCurrentlyViewedPath(MainViewManager.ACTIVE_PANE);                  if  (curFullPath  &&  _hasFileSelectionFocus())  {                          actionCreator.setSelected(curFullPath,  true);                  }  else  {                          actionCreator.setSelected(null);                  }                  _fileViewControllerChange();          }    
  • 19.
  • 22.        function  render(element,  viewModel,  projectRoot,  actions,  forceRender,  platform)  {                  if  (!projectRoot)  {                          return;                  }                    React.renderComponent(fileTreeView({                          treeData:  viewModel.treeData,                          selectionViewInfo:  viewModel.selectionViewInfo,                          sortDirectoriesFirst:  viewModel.sortDirectoriesFirst,                          parentPath:  projectRoot.fullPath,                          actions:  actions,                          extensions:  _extensions,                          platform:  platform,                          forceRender:  forceRender                  }),                              element);          }
  • 23.        var  fileTreeView  =  React.createClass({                    /**                    *  Update  for  any  change  in  the  tree  data  or  directory  sorting  preference.                    */                  shouldComponentUpdate:  function  (nextProps,  nextState)  {                          return  nextProps.forceRender  ||                                  this.props.treeData  !==  nextProps.treeData  ||                                  this.props.sortDirectoriesFirst  !==  nextProps.sortDirectoriesFirst  ||                                  this.props.extensions  !==  nextProps.extensions  ||                                  this.props.selectionViewInfo  !==  nextProps.selectionViewInfo;                  },
  • 24.                render:  function  ()  {                          var  contents  =  directoryContents({                                          isRoot:  true,                                          parentPath:  this.props.parentPath,                                          sortDirectoriesFirst:  this.props.sortDirectoriesFirst,                                          contents:  this.props.treeData,                                          extensions:  this.props.extensions,                                          actions:  this.props.actions,                                          forceRender:  this.props.forceRender,                                          platform:  this.props.platform                                  });                                                    return  DOM.div(                                  null,                                  selectionBackground,                                  contextBackground,                                  extensionForSelection,                                  extensionForContext,                                  contents                          );                  }          });
  • 25.        directoryContents  =  React.createClass({                  render:  function  ()  {                          var  extensions  =  this.props.extensions,                                  iconClass  =  extensions  &&  extensions.get("icons")  ?  "jstree-­‐icons"  :  "jstree-­‐no-­‐ icons",                                  ulProps  =  this.props.isRoot  ?  {                                          className:  "jstree-­‐brackets  jstree-­‐no-­‐dots  "  +  iconClass                                  }  :  null;                            var  contents  =  this.props.contents,                                  namesInOrder  =  _sortDirectoryContents(contents,  this.props.sortDirectoriesFirst);
  • 26.                        return  DOM.ul(ulProps,  namesInOrder.map(function  (name)  {                                  var  entry  =  contents.get(name);                                    if  (FileTreeViewModel.isFile(entry))  {                                          return  fileNode({                                                  parentPath:  this.props.parentPath,                                                  name:  name,                                                  entry:  entry,                                                  actions:  this.props.actions,                                                  extensions:  this.props.extensions,                                                  forceRender:  this.props.forceRender,                                                  platform:  this.props.platform,                                                  key:  name                                          });                                  }  else  {                                          return  directoryNode({
  • 27.        directoryNode  =  React.createClass({                  mixins:  [contextSettable,  pathComputer,  extendable],                  render:  function  ()  {                          var  entry  =  this.props.entry,                          if  (entry.get("rename"))  {                                  renameInput  =  directoryRenameInput({                                          actions:  this.props.actions,                                          entry:  this.props.entry,                                          name:  this.props.name,                                          parentPath:  this.props.parentPath                                  });                          }
  • 28.        directoryNode  =  React.createClass({                  render:  function  ()  {                          return  DOM.li({                                  className:  this.getClasses("jstree-­‐"  +  nodeClass),                                  onClick:  this.handleClick,                                  onMouseDown:  this.handleMouseDown                          },                                  DOM.ins({                                          className:  "jstree-­‐icon"                                  },  "  "),                                  renameInput,                                  nameDisplay,                                  childNodes);                  }          });
  • 29.                handleClick:  function  (event)  {                          var  isOpen  =  this.props.entry.get("open"),                                  setOpen  =  isOpen  ?  false  :  true;                            if  (event.metaKey  ||  event.ctrlKey)  {                                  //  ctrl-­‐alt-­‐click  toggles  this  directory  and  its  children                                  if  (event.altKey)  {                                          if  (setOpen)  {                                                  //  when  opening,  we  only  open  the  immediate  children  because                                                  //  opening  a  whole  subtree  could  be  really  slow  (consider                                                  //  a  `node_modules`  directory,  for  example).                                                  this.props.actions.toggleSubdirectories(this.myPath(),  setOpen);                                                  this.props.actions.setDirectoryOpen(this.myPath(),  setOpen);                                          }  else  {                                                  //  When  closing,  we  recursively  close  the  whole  subtree.                                                  this.props.actions.closeSubtree(this.myPath());                                          }                                  }  else  {
  • 30.        ActionCreator.prototype.toggleSubdirectories  =  function  (path,  openOrClose)  {                  this.model.toggleSubdirectories(path,  openOrClose).then(_saveTreeState);          };
  • 31.        ProjectModel.prototype.toggleSubdirectories  =  function  (path,  openOrClose)  {                  var  self  =  this,                          d  =  new  $.Deferred();                    this.setDirectoryOpen(path,  true).then(function  ()  {                          var  projectRelativePath  =  self.makeProjectRelativeIfPossible(path),                                  childNodes  =  self._viewModel.getChildDirectories(projectRelativePath);                                                    Async.doInParallel(childNodes,  function  (node)  {                                  return  self.setDirectoryOpen(path  +  node,  openOrClose);                          },  true).then(function  ()  {                                  d.resolve();                          },  function  (err)  {                                  d.reject(err);                          });                  });                                    return  d.promise();          };
  • 32.        ProjectModel.prototype.setDirectoryOpen  =  function  (path,  open)  {                  var  projectRelative  =  this.makeProjectRelativeIfPossible(path),                          needsLoading        =  !this._viewModel.isPathLoaded(projectRelative),                          d                              =  new  $.Deferred(),                          self                        =  this;                  if  (open  &&  needsLoading)  {                          var  parentDirectory  =  FileUtils.getDirectoryPath(FileUtils.stripTrailingSlash(path));                          this.setDirectoryOpen(parentDirectory,  true).then(function  ()  {                                  self._getDirectoryContents(path).then(onSuccess).fail(function  (err)  {                                          d.reject(err);                                  });                          },  function  (err)  {                                  d.reject(err);                          });                  }  else  {                          onSuccess();                  }
  • 33.                function  onSuccess(contents)  {                          //  Update  the  view  model                          if  (contents)  {                                  self._viewModel.setDirectoryContents(projectRelative,  contents);                          }                            if  (open)  {                                  self._viewModel.openPath(projectRelative);                                  if  (self._focused)  {                                          var  currentPathInProject  =  self.makeProjectRelativeIfPossible(self._currentPath);                                          if  (self._viewModel.isFilePathVisible(currentPathInProject))  {                                                  self.setSelected(self._currentPath,  true);                                          }  else  {                                                  self.setSelected(null);                                          }                                  }                          }  else  { branches to test
  • 34.        FileTreeViewModel.prototype.openPath  =  function  (path)  {                  this._commit(_openPath(this._treeData,  path));          };
  • 35. {          "subdir":  {                  open:  true,                  children:  {                          "afile.js":  {},                          "subsubdir":  {                                  children:  {                                          "thirdsub":  {                                                  children:  {}                                          }                                  }                          }                  }          }   } subdir/subsubdir/thirdsub/
  • 36. {          "subdir":  {                  open:  true,                  children:  {                          "afile.js":  {},                          "subsubdir":  {                                  children:  {                                          "thirdsub":  {                                                  children:  {}                                          }                                  }                          }                  }          }   } subdir/subsubdir/thirdsub/ treeData.subdir.subsubdir.open  =  true;   treeData.subdir.subsubdir.thirdsub.open  =  true;
  • 37. thirdsub  =  treeData.subdir.subsubdir.thirdsub;   newThirdSub  =  thirdsub.set("open",  true);     thirdsub  !==  newThirdSub;  //  true   treeData.subdir.subsubdir.thirdsub  !==  newThirdSub  //  true   treeData.subdir.subsubdir.thirdsub.open  ===  undefined;  //  true
  • 38. Attack of the clones https://www.flickr.com/photos/hjmediastudios/7910348016/
  • 39. thirdsub  =  treeData.subdir.subsubdir.thirdsub;   newThirdSub  =  thirdsub.set("open",  true);     thirdsub  !==  newThirdSub;  //  true   treeData.subdir.subsubdir.thirdsub  !==  newThirdSub  //  true   treeData.subdir.subsubdir.thirdsub.open  ===  undefined;  //  true
  • 40. thirdsub  =  treeData.subdir.subsubdir.thirdsub;   newThirdSub  =  thirdsub.set("open",  true);   newSubSubDir  =  subsubdir.set("thirdsub",  newThirdSub);   newSubDir  =  subdir.set("subsubdir",  newSubSubDir);   treeData  =  treeData.set("subdir",  newSubDir);
  • 41.        function  _openPath(treeData,  path)  {                  var  objectPath  =  _filePathToObjectPath(treeData,  path);                  function  setOpen(node)  {                          return  node.set("open",  true);                  }                  while  (objectPath  &&  objectPath.length)  {                          var  node  =  treeData.getIn(objectPath);                          if  (isFile(node))  {                                  objectPath.pop();                          }  else  {                                  if  (!node.get("open"))  {                                          treeData  =  treeData.updateIn(objectPath,  setOpen);                                  }                                  objectPath.pop();                                  if  (objectPath.length)  {                                          objectPath.pop();                                  }                          }                  }                  return  treeData;          } mutable?
  • 42.        FileTreeViewModel.prototype.openPath  =  function  (path)  {                  this._commit(_openPath(this._treeData,  path));          };
  • 43.        FileTreeViewModel.prototype._commit  =  function  (treeData,  selectionViewInfo)  {                  var  changed  =  false;                  if  (treeData  &&  treeData  !==  this._treeData)  {                          this._treeData  =  treeData;                          changed  =  true;                  }                                    if  (selectionViewInfo  &&  selectionViewInfo  !==  this._selectionViewInfo)  {                          this._selectionViewInfo  =  selectionViewInfo;                          changed  =  true;                  }                  if  (changed)  {                          $(this).trigger(EVENT_CHANGE);                  }          };
  • 44. –C.A.R. Hoare “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”
  • 46. simple! composed of one thing, not combined
  • 48. Simple Architecture • Each part does one thing with side effects in few, known places • React lets you generate a whole UI functionally • Immutable-JS allows you to control when data updates occur • Every part of your application can have a consistent state • Object identity tells you when something has changed
  • 49. A modern, open source text editor that understands web design. http://brackets.io