         if next.project_update_tag != @props.project_update_tag
             parse_project_search_string.clear_cache()
             hashtags.clear_cache()
-
+        @update_project_list(next.project_map)
+
+    update_project_list: (project_map) ->
+        @_project_list = (project for project_id, project of project_map)
+        for p in @_project_list
+            p.hashtags = parse_project_tags(p)
+            # also search string
+            # and only do this to the projects that change -- use immutable js to know
+        @_project_list.sort (p0, p1) -> -misc.cmp(p0.last_edited, p1.last_edited)
+        return @_project_list
+
+    project_list: ->
+        return @_project_list ? @update_project_list(@props.project_map)
 
     # Takes a project and a list of search terms, returns true if all search terms exist in the project
     matches: (project, search_terms) ->

            marginBottom : '1ex'
        <div style={projects_title_styles}><Icon name="thumb-tack" /> Projects </div>


    render: ->
        <Grid fluid className="constrained">
            <Well style={marginTop:'1em'}>
                <Row>

        return true

    visible_projects: ->
        words = misc.split(@props.search.toLowerCase()).concat(k for k of @props.selected_hashtags[@filter()])
        return (project for project in @project_list() when project_is_in_filter(project, @props.hidden, @props.deleted) and @matches(project, words))

    toggle_hashtag: (tag) ->
        selected_hashtags = JSON.parse(JSON.stringify(@props.selected_hashtags))
