HistoryJSMixin updated: React History / PushState now with URL updates (great for pagination / search)

I wanted a simple way for React component state to update pushState/history, and I found it in Leif Denby's simple HistoryJSMixin. One thing it was missing: being able to update the URL too (e.g. change "/foo" to "/foo?bar=baf") with that state. 

I've added it with a simple two line change here (line 57 and 61): 

/** Adapted from http://leifdenby.svbtle.com/reactjs-and-browser-history-a-historyjs-mixin */
var HistoryJSMixin = {
_historyjs_recoverState: function(state) {
received_state_serialized = state.data;
if (!$.isEmptyObject(received_state_serialized)) {
if (this.deserializeState !== undefined) {
received_state = this.deserializeState(received_state_serialized);
}
else {
received_state = received_state_serialized;
}
if (this.serializeState !== undefined) {
current_state_serialized = this.serializeState();
}
else {
current_state_serialized = JSON.stringify(this.state);
}
// look through the received state to see if there are any changes
// relative to the state we are currently in
combined_state = $.extend({}, this.state, received_state);
update_state = !(JSON.stringify(current_state_serialized) === JSON.stringify(received_state_serialized));
var callback_f = function() {};
if (this.hasRecoveredState !== undefined) {
callback_f = this.hasRecoveredState;
}
if (update_state) {
if (this.recoverState !== undefined) {
this.recoverState(combined_state, callback_f);
}
else {
this.setState(combined_state, callback_f);
}
}
}
},
saveState: function(options) {
var serialized_state = null;
if (this.serializeState !== undefined) {
serialized_state = this.serializeState();
}
else {
serialized_state = {};
$.each(this.state, function(k,v) {
if (k != '_historyjs_has_saved') {
serialized_state[k] = String(v);
}
});
}
if (!this.state._historyjs_has_saved) {
this.setState({ _historyjs_has_saved: true }, function() {
History.replaceState(serialized_state, options.title, options.url);
});
}
else {
History.pushState(serialized_state, options.title, options.url);
}
},
bindToBrowserHistory: function() {
History.Adapter.bind(window,'statechange',function(){
this._historyjs_recoverState(History.getState());
}.bind(this));
this._historyjs_recoverState(History.getState());
},
getInitialState: function() {
return { _historyjs_has_saved: false };
},
patchSavedState: function(data, callback) {
var old_state = History.getState();
$.extend(old_state.data, data);
History.replaceState(old_state.data);
if (callback !== undefined) {
callback();
}
},
};

Here's an example snippet of how to use it:

@ExampleComponent = React.createClass
mixins: [HistoryJSMixin]
# so, in your search field, you can save the state to history and set the URL as well below...
#
search: (toSearch)->
handleResults = (err, content)=>
if content.query == @state.search
@setState(searchResults: content.hits)
@saveState(url: '/foo?bar=foo') # BINGO
# hypothetical search function async call../
@index.search toSearch, {}, handleResults

So, as you can see on line 10, you can pass a url key to the call now and have it propagate to the URL bar. I've been using it in places where React Router would be too heavy duty, and all you really want is to propagate search terms, or page number.

LazyGenerate: How to lazily generate properties in ActiveRecord

At times in the Posterous days we wanted to add new properties but a migration would be prohibitively expensive given a large corpus of data.

Here's a simple module you can include in your ActiveRecord objects to do just that — lazily generate potentially costly computation and save it to an object only when it is actually called. 

Dragons in these hills — but sometimes you want that. We're going to steer clear of these kinds of hacks for Posthaven, but as a minor exercise in metaprogramming maybe this'll be useful in the future:

# LazyGenerate makes it easy to make new ActiveRecord objects with accessors
# that are generated and saved lazily
#
# To use, add this to your ActiveRecord class:
# extend LazyGenerate
# lazy_generate :my_lazy_property, :generate_lazy_property
#
# Side effect: Save is called on the object upon generation.
#
module LazyGenerate
def lazy_generate(column, generate_method)
class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}_with_laziness
if existing_value = #{column}_without_laziness
existing_value
else
self.#{column} = new_value = #{generate_method}
save
new_value
end
end
alias_method_chain :#{column}, :laziness
RUBY
end
end
require 'spec_helper'
describe LazyGenerate do
describe '.lazy_generate' do
class LazyGenerateTest
extend LazyGenerate
attr_accessor :value
def generator
'xyz987'
end
def save; end
lazy_generate :value, :generator
end
context 'with an existing value' do
it 'should pass the value if already set' do
obj = LazyGenerateTest.new
obj.value = 'abc123'
obj.value.should eql 'abc123'
end
end
context 'with an existing value' do
it 'should generate the value if not already set' do
LazyGenerateTest.new.value.should eql 'xyz987'
end
end
end
end