• Stars
    star
    141
  • Rank 259,971 (Top 6 %)
  • Language
    JavaScript
  • Created over 10 years ago
  • Updated over 10 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

React-based Imgur-like isomorphic demo app

Imgsible

Imgsible is an image upload and sharing site in the spirit of the excellent image sharing site Imgur built to demonstrate building a non-trivial isomorphic application with React.

Screenshot

Check out the demo on YouTube.

Running

Prerequisites:

  • Node.js
  • Redis
  • GraphicsMagick

On OS X, these are all available via Homebrew.

Once you have satisfied the prerequisites, you can install and start the application. Clone the app, and from its directory run:

  1. npm install
  2. redis-server conf/redis.development.conf (in another console, or in the background)
  3. npm start

The client bundle will automatically build and the web server will start. Use the PORT environment variable if you want to specify the port the HTTP server binds to.

Architecture

Note: This is in no way the ideal architecture. It has some drawbacks; see the "Pros" and "Cons" sections, below, for details. It does, however, solve some of my problems, so here it is. :)

When a view needs to update the application-level state--for instance, when the user interacts with the UI--it uses the Dispatcher. The dispatcher is an object that references one or more Stores and dispatches Actions to each of them.

Stores are added to the Dispatcher via its register method. The Store must implement a handleDispatch method, taking type and data parameters, to handle Actions dispatched from the Dispatcher. handleDispatch must return the portion of the application state that the Store manages (or a promise of that state if the handler is asynchronous).

Actions are dispatched via the Dispatcher's dispatch method. A set of objects--normally one per Store--provide methods to generate Actions. An Action's type simply identifies it, providing a way for Stores to determine if they care about the Action; an Action's data can be anything.

Dispatch Phase 1

Once every piece of state has been resolved from the promises returned from the Stores' handleDispatch methods, they are merged together to form the object that will be passed to React's setState method. In addition, the dispatch method returns a promise of this merged state--this is mostly useful on the server, where you must know when the state is ready so you can call renderComponentToString.

Dispatch Phase 2

function ImageStore() {}

ImageStore.prototype.handleDispatch = function(type, data) {
  // ...
  return myStateOrAPromiseOfMyState;
};

var dispatcher = new Dispatcher();
dispatcher.register(new ImageStore());
dispatcher.register(someOtherStore);

var statePromise = dispatcher.dispatch(ImageActions.loadImage(imageId));
statePromise.then(function(state) {
  console.log('The new state will be:', state);
});

The Dispatcher is an EventEmitter; the top-level React component for an application can listen for the stateUpdate event which is emitted when the promises for an Action are resolved, and provides the same data as the promise returned from dispatch.

Once the top-level component calls setState, the updated state flows downward to child components, none of which have access to the Stores directly. Since there is a period of time between a view dispatching an Action and that Action resulting in a new state being emitted, it's important that components are able to render in the absence of that state. For example, the ImageView component shows a loading message if it doesn't yet have its image data.

Accessing the Dispatcher

A React mixin called DispatcherMixin is used to provide a dispatch property on each component it is included in. For it to work, the very top-level component that includes the mixin must have a dispatcher property that references a Dispatcher. All child components that include the mixin will receive the property automatically, and do not need to have a property referencing the Dispatcher.

Server Rendering

On the server, we set up the Stores so that they use a server-appropriate strategy for responding to events. When a request comes in for a particular URL, we trigger the appropriate Actions for that page and collect the promises returned. When they are all resolved, we render the top-level component as a string and pass in the merged state as a property.

Additionally, the merged state is provided to the client via a property on window so that the client-app can boot with the same data. This gives us server rendering of the application with a transparent upgrade to a fully functional front-end React app when the JavaScript loads.

Pros

  • Components don't have to know about the Stores at all--they are coupled only by the state that the Stores generate.
  • Data flow is still top-down and one-directional from the components perspective. A request for change goes out to the Dispatcher and the new state flows in to the top-level component to flow down to children. Components do not reference stores at all.
  • Stores can easily be passed parameters to change their behavior on the client and server. For example, the ImageStore takes an ImageDb as a parameter. The server version uses Redis, while the client version makes XHR requests. They otherwise have the same API and use promises, so the Store need not know the difference. Adding new features that work on both the client and the server is surprisingly painless.

Cons

  • The state emitted by a Store and the components that use that state are tightly coupled. There should probably be an intermediate layer that converts arbitrary data from stores into state usable by React.
  • Stores cannot use information from one another. For example, one Action tells the RouteStore to update the route portion of the state, while a separate Action tells the ImageStore to load the image data for a given image. There is no way for the RouteStore to use some of the image data--say, the title--to update a portion of its state. Ideally, dependencies could be set up between the stores, which would also remove the need to fire multiple Actions to get the UI in a certain state.
  • The Dispatcher can potentially get a bit confused if multiple Actions are dispatched very close together and one of the Stores is slow to resolve its promise. The race condition caused by the ordering of the promises being resolved can end up setting state that is not accurate. In practice (so far) this isn't a huge issue because most Actions only affect one store and long-running asynchronous actions tend to be relatively far apart.
  • It's difficult for Stores to push data to the UI when their state changes asynchronously--for example, an Action that starts a file upload returns a single promise, but it may be useful to provide multiple UI updates as the upload progresses. Currently, the Dispatcher has a method called refreshState that calls getState on each Store and merges them together, but this is not a great long-term solution.
  • I'm not a huge fan of the way Actions are currently generated. In particular, converting the arguments into an object and then passing that object to the Stores' handleDispatch methods is error prone; passing them as arguments all the way down the chain (using handleDispatch.apply) would likely be better.
  • On the server, we must dispatch the right Actions to get the stores in a proper state. Ideally, we could provide a mechanism to preload the stores on the server without dispatching any Actions. This would probably require decoupling the Stores and the state (as mentioned above).

See this pull request for progress on an architecture overhaul that should help alleviate these issues.

But what about...

  • Why not use react-async and react-router-component? One of my goals with this app was to determine how complicated things like routing and asynchronous data loading were, so I built them by hand. The aforementioned libraries, as well as many others, are great resources.
  • Why is routing handled by a Store? I thought it would be interesting to see how it would look if the route portion of the application state was handled by a Store like any other piece of the state.

More Repositories

1

fluxxor

🛠️ Flux architecture tools for React
JavaScript
1,693
star
2

planetary.js

🌎 Awesome interactive globes for the web
JavaScript
1,586
star
3

react-primer

Short annotated examples of React concepts
JavaScript
380
star
4

chrome-fast-tab-switcher

⌨️ React-based extension to quickly switch to other open Chrome tabs with just your keyboard
JavaScript
345
star
5

toml-node

TOML parser for Node.js and the Browser. Parses TOML v0.4.0
JavaScript
306
star
6

battlenet

Easily consume Blizzard's Community Platform API.
Ruby
58
star
7

live-twitter-map

Node.js server and client page to show geo-tagged Tweets live on a map
CoffeeScript
37
star
8

electron-youtube-player

A YouTube playlist manager and video player in Electron w/ React + Redux
JavaScript
30
star
9

toml-require

require() .toml files in Node.js
JavaScript
22
star
10

isomorphic-fluxxor-experiment

Testing an isomorphic Fluxxor app
JavaScript
20
star
11

faye-sinatra

Playing around with Faye and Sinatra
Ruby
17
star
12

es7-autobinder

Automatically bind methods in ES6 classes to 'this' with ES7 decorators
JavaScript
14
star
13

wow-api-projects

Generator for the thread at http://us.battle.net/wow/en/forum/topic/2369882588#1
Ruby
14
star
14

chatterbox

A multi-room chat site in Rails using Faye and PrivatePub
JavaScript
14
star
15

react-atomshell-spotify

Exploring desktop apps using Atom Shell, React, and Babel
JavaScript
12
star
16

react-babel-boilerplate

Eaisly and quickly create: Web applications w/ React + Babel + HMR -or- Electron apps w/ React + Babel
JavaScript
12
star
17

thonk

What do you thonk?
JavaScript
10
star
18

atom-mocha-test-runner

Run your Atom package tests using Mocha
JavaScript
10
star
19

electron-markdown

Convert GitHub-flavored Markdown to HTML with a few extras
JavaScript
9
star
20

placekitten

A small Ruby library to generate placekitten images
Ruby
9
star
21

node-cmark-gfm

Node.js bindings to GitHub's GFM-enhanced fork of cmark, the CommonMark reference implementation in C
JavaScript
9
star
22

kinetophone

Stitch together and play back time-sequenced events with durations
JavaScript
8
star
23

gwt-wizard

A flexible GWT wizard widget for your project
Java
7
star
24

wow-realm-status-js

WoW Realm Status: Backbone Edition
CoffeeScript
7
star
25

poke-colors

Pokémon color distribution
JavaScript
7
star
26

test-until

Utility method that returns a promise that resolves when a condition returns true
JavaScript
7
star
27

coffeescript-primer

Detailing some of the cooler features of CoffeeScript
6
star
28

trie-hard

A JavaScript trie implementation for Node.js and the browser
JavaScript
6
star
29

dci-ruby

Playing with DCI in Ruby
Ruby
6
star
30

covfefe

Have access to our president's idiocy without leaving your editor
CoffeeScript
6
star
31

rforce-wrapper

RForce-wrapper creates a wrapper around RForce that more clearly exposes the Web Services API
Ruby
6
star
32

sublime-user-package

My Sublime Text 2 User Package
Python
6
star
33

michelletilley-blog

The blog at michelletilley.net
CSS
5
star
34

react-workshop

Let's learn us some React!
JavaScript
5
star
35

rails-book

Sample application from the railstutorial.org book
Ruby
5
star
36

distributed-flux

Using a Flux-like architecture for distributed programs
JavaScript
5
star
37

judaw

Easier connecting to and querying from your UniData database via Java
Java
4
star
38

angular-annotate-sweetjs

Angular DI annotation via Sweet.js macros
JavaScript
4
star
39

so-jest-react-mock-example

Example for SO Question #25533036
JavaScript
4
star
40

so_association_expirement

Code for SO question 8205284
Ruby
4
star
41

moodle-tools

Helpful command-line scripts for administering Moodle installations
Python
4
star
42

tip-of-branch

A GitHub Action to ensure the triggering commit is still at the tip of a given branch
JavaScript
4
star
43

dotfiles

My dotfiles
Vim Script
3
star
44

react-kinetophone

React-based player for the Kinetophone library
JavaScript
3
star
45

functional-reactive-calculator-js

Calculator using functional reactive programming techniques in JavaScript
CSS
3
star
46

wow-realm-status

WoW Realm Status: Ruby Edition
Ruby
3
star
47

MovieKue

AngularJS tutorial application
JavaScript
3
star
48

git-pear

🍐 Tool for setting the Git author and committer when pairing in Atom :atom:
JavaScript
3
star
49

dataloaderb

Easily create, run, and extend Apex Data Loader processes on Windows via Ruby
Ruby
3
star
50

wow-realm-status-angular

WoW Realm Status: AngularJS Edition
CoffeeScript
3
star
51

angular-email-ui

Experimenting with creating a rich email UI using AngularJS
JavaScript
3
star
52

action-bundlesize

Get info on bundle size changes
JavaScript
3
star
53

350mission-webcam

Keeping an eye on the new building at 350 Mission
CoffeeScript
3
star
54

hubot-rpc-gen

Generate Chatops RPC compatible servers for Hubot and more!
JavaScript
3
star
55

better-prop-types

DSL for less verbose propTypes declarations in React
JavaScript
2
star
56

BinaryMuse

2
star
57

elm-asteroids

Asteroids in Elm
Elm
2
star
58

redux-message-passer

Keep Redux stores in sync via message passing
JavaScript
2
star
59

advent-of-code-2020

Rust
2
star
60

wowheadr

Leverage the power of Wowhead for your Ruby projects.
Ruby
2
star
61

elastic-movies

Testing Elasticsearch against a database of movie information
Ruby
2
star
62

Dreamstill

JavaScript
2
star
63

contributify

Programmatically generate a GitHub contribution graph
TypeScript
2
star
64

timex

Small timer module using requestAnimationFrame and performance.now when available
JavaScript
2
star
65

react-chip8-emulator

Experimeting with the Chip-8 project from The Great Code Club in React
JavaScript
2
star
66

boggle-solver

Solve all the Boggles!
CoffeeScript
2
star
67

tree-view

Atom file browser
CoffeeScript
2
star
68

UDJrb

Classes and extensions to make working with the UniData Java API via JRuby easier.
Ruby
2
star
69

wafflejs-native-node-addons

Links and resources from my Waffle.js talk on native Node addons
C++
2
star
70

advent-of-code-2019

Rust
2
star
71

tip-of-branch-testing

HCL
2
star
72

fader

JavaScript
2
star
73

ruby-in-the-browser

Experimenting with Ruby in the browser.
Ruby
2
star
74

wow-api-documentation

Documentation for the Blizzard Community Platform API
JavaScript
2
star
75

express-app-template

My most commonly used Express app in an easy-to-clone template
CoffeeScript
2
star
76

wow-realm-status-react

WoW Realm Status: React Edition
JavaScript
2
star
77

atom-fortune-background-tips

Displays output from the 'fortune' program in the background when there are no editors open
CoffeeScript
2
star
78

x-tag-react-hello-world

x-tag + react = ♥ ?
1
star
79

xkcd-sinatra

Sinatra app to display xkcd comics so that the alt text can be seen
Ruby
1
star
80

battlemasters-lfg

LFG tool for Battlemasters.org
Ruby
1
star
81

electron-react-devtools-repro

JavaScript
1
star
82

Executioner

A test project; Executioner controls processes from a web-based interface
CoffeeScript
1
star
83

so_5049994

Code for StackOverflow question 5049994
Ruby
1
star
84

lumos

Raspberry Pi media screen software, powered by Electron and React
JavaScript
1
star
85

wow-gems

Find the WoW gem you're looking for
JavaScript
1
star
86

react-isomorphic-test

Testing an isomorphic React application
JavaScript
1
star
87

Broker_GemGuild

My version of http://wow.curse.com/downloads/wow-addons/details/gemguild.aspx
Lua
1
star
88

yubikiri

Fetch data using Promises. Pinky swear!
JavaScript
1
star
89

connect-compress-issue

JavaScript
1
star
90

so_5521473_autocomplete

Sample app for SO question 5521473
Ruby
1
star
91

eztd-prototype

Prototype for EZTD mockup
Ruby
1
star
92

dice-dist

JavaScript
1
star
93

fluxbox

Moved to https://github.com/BinaryMuse/fluxxor
1
star
94

kingsplayers-site

The previous (ancient) web site for the Kings Players at kingsplayers.net
JavaScript
1
star
95

kingsplayers-net

The new kingsplayers.net site
CSS
1
star
96

21_card_trick

The 21 Card Trick in Ruby
Ruby
1
star
97

gowiki

Wiki site in Go (tutorial project)
Go
1
star
98

libuv-experiments

Let's learn some libuv
C++
1
star
99

reactk

React GTK+3 Bindings (Experimental)
TypeScript
1
star
100

ts-octokit-issue

TypeScript
1
star