diff --git a/src/createIntlObject.js b/src/createIntlObject.js index 20cd197..bc0accc 100644 --- a/src/createIntlObject.js +++ b/src/createIntlObject.js @@ -1,6 +1,11 @@ +import getValue from './utils/getValue'; import formatMessage from './formatMessage'; -export default (intl) => ({ +function createIntlObject(intl) { + return { formatMessage: formatMessage(intl), - locale: intl.locale -}); + locale: getValue(intl, 'locale') + } +} + +export default createIntlObject; diff --git a/src/createMapStateToProps.js b/src/createMapStateToProps.js index 21523fd..d650ead 100644 --- a/src/createMapStateToProps.js +++ b/src/createMapStateToProps.js @@ -1,14 +1,15 @@ +import getValue from './utils/getValue'; import createIntlObject from './createIntlObject'; import intlSelector from './intlSelector'; -export function createMapStateToProps(selector = intlSelector) { +function createMapStateToProps(selector = intlSelector) { return (state) => { const intl = selector(state); return { intl: createIntlObject(intl), - key: intl.locale - }; + key: getValue(intl, 'locale') + } }; } diff --git a/src/formatMessage.js b/src/formatMessage.js index fabb051..58f8fa7 100644 --- a/src/formatMessage.js +++ b/src/formatMessage.js @@ -1,40 +1,22 @@ import invariant from 'invariant'; -import get from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; import isFunction from 'lodash/isFunction'; +import getMessage from './utils/getMessage'; +import getValue from './utils/getValue'; import {LOG_PREFIX} from './constants'; const ENV = process.env.NODE_ENV; -function isImmutable(state) { - return isFunction(state.get) - && isFunction(state.getIn); -} - -function getLocale(state) { - if (isImmutable(state)) { - return state.get('locale'); - } - return state.locale; -} - -function parseMessage(message, values = {}) { +const parseMessage = (message, values = {}) => { if (message && isFunction(message)) { return message(values); } return void 0; } -function getMessage(state, id) { - if (isImmutable(state)) { - return state.getIn(['messages', id], ''); - } - return get(state, ['messages', id], ''); -} - function formatMessage(state = {}) { - const locale = getLocale(state); + const locale = getValue(state, 'locale'); if (!locale) { return () => ''; diff --git a/src/injectIntl.js b/src/injectIntl.js index 3d7907b..650f293 100644 --- a/src/injectIntl.js +++ b/src/injectIntl.js @@ -2,7 +2,7 @@ import invariant from 'invariant'; import {LOG_PREFIX} from './constants'; import createMapStateToProps from './createMapStateToProps'; -export function injectIntl( +function injectIntl( connect, mapStateToProps = createMapStateToProps() ) { diff --git a/src/intlReducer.js b/src/intlReducer.js index a07a720..11d0eab 100644 --- a/src/intlReducer.js +++ b/src/intlReducer.js @@ -1,22 +1,34 @@ +import isImmutable from './utils/isImmutable'; import {UPDATE_ACTION} from './constants'; -export const initialState = () => ({ - cacheDisable: false, - ecmaSupport: false, - locale: '', - messages: {} -}); +function initialState() { + return { + cacheDisable: false, + ecmaSupport: false, + locale: '', + messages: {} + } +} + +function setImmutable(state, payload) { + return state.merge(payload); +} -export function intlReducer(state = initialState(), action = {}) { +function intlReducer(state = initialState(), action = {}) { - if (action && action.type === UPDATE_ACTION) { - return { - ...state, - ...action.payload - }; + if (!action || action.type !== UPDATE_ACTION) { + return state; } - return state; + if (isImmutable(state)) { + return setImmutable(state, action.payload); + } + + return { + ...state, + ...action.payload + } } +export {initialState as initialState}; export default intlReducer; diff --git a/src/intlSelector.js b/src/intlSelector.js index 30f4497..0c07ce9 100644 --- a/src/intlSelector.js +++ b/src/intlSelector.js @@ -1,5 +1,6 @@ import MessageFormat from 'messageformat'; import isEmpty from 'lodash/isEmpty'; +import isImmutable from './utils/isImmutable'; const isIntlValid = (intl) => ( !isEmpty(intl) && intl.locale @@ -16,11 +17,17 @@ const compileMessages = (intl) => ( .compile(intl.messages) ); +const getIntl = (state) => ( + isImmutable(state.intl) + ? state.intl.toJS() + : state.intl +); + export function createIntlSelector() { let cache = {}; return (state = {}) => { - const {intl} = state; + const intl = getIntl(state); if (!isIntlValid(intl)) { cache = {} diff --git a/src/updateIntl.js b/src/updateIntl.js index 74deb07..fe72d8f 100644 --- a/src/updateIntl.js +++ b/src/updateIntl.js @@ -4,11 +4,11 @@ import {initialState} from './intlReducer'; const ACCEPTABLE_KEYS = Object.keys(initialState()); -export function updateIntl(keys = {}) { +function updateIntl(keys = {}) { return { type: UPDATE_ACTION, payload: pick(keys, ACCEPTABLE_KEYS) - }; + } } export default updateIntl; diff --git a/src/utils/getMessage.js b/src/utils/getMessage.js new file mode 100644 index 0000000..b3c16d2 --- /dev/null +++ b/src/utils/getMessage.js @@ -0,0 +1,11 @@ +import get from 'lodash/get'; +import isImmutable from './isImmutable'; + +function getMessage(state, id) { + if (isImmutable(state)) { + return state.getIn(['messages', id], ''); + } + return get(state, ['messages', id], ''); +} + +export default getMessage; diff --git a/src/utils/getValue.js b/src/utils/getValue.js new file mode 100644 index 0000000..be649bc --- /dev/null +++ b/src/utils/getValue.js @@ -0,0 +1,11 @@ +import get from 'lodash/get'; +import isImmutable from './isImmutable'; + +function getValue(state, value) { + if (isImmutable(state)) { + return state.get(value); + } + return get(state, value); +} + +export default getValue; diff --git a/src/utils/isImmutable.js b/src/utils/isImmutable.js new file mode 100644 index 0000000..ca025d3 --- /dev/null +++ b/src/utils/isImmutable.js @@ -0,0 +1,7 @@ +import isFunction from 'lodash/isFunction'; + +function isImmutable(state = {}) { + return isFunction(state.toJS); +} + +export default isImmutable; diff --git a/tests/intlReducer.spec.js b/tests/intlReducer.spec.js index c73803f..8233273 100644 --- a/tests/intlReducer.spec.js +++ b/tests/intlReducer.spec.js @@ -1,4 +1,5 @@ import {expect} from 'chai'; +import {fromJS} from 'immutable'; import {UPDATE_ACTION} from '../src/constants'; import intlReducer, {initialState} from '../src/intlReducer'; @@ -27,6 +28,16 @@ describe('intlReducer', function () { expect(result).to.deep.eql(expected); }); + it('updates state even if immutable', function () { + const action = { + type: UPDATE_ACTION, + payload: getUpdatePayload() + } + const expected = fromJS(getUpdatePayload()); + const result = intlReducer(fromJS(initialState()), action); + expect(result.toJS()).to.deep.equal(expected.toJS()); + }); + function getUpdatePayload() { return { cacheDisable: false, diff --git a/tests/intlSelector.spec.js b/tests/intlSelector.spec.js index 5755b46..e28f703 100644 --- a/tests/intlSelector.spec.js +++ b/tests/intlSelector.spec.js @@ -1,4 +1,5 @@ import {expect} from 'chai'; +import {fromJS} from 'immutable'; import mockMessageFormat from './mocks/messageformat'; import defaultIntlSelector, {createIntlSelector} from '../src/intlSelector'; @@ -36,6 +37,30 @@ describe('intlSelector', function () { expect(round2.messages.alternate).to.be.undefined; }); + it('returns empty object when unable to locale intl even when state is immutable', function () { + const intlSelector = createIntlSelector(); + const result = intlSelector({ + intl: fromJS({}) + }); + expect(result).to.be.empty; + }); + + it('returns cache when cache intl and reducer intl is the same even when state is immutable.', function () { + const intlSelector = createIntlSelector(); + + let state1 = getState(); + state1.intl = fromJS(state1.intl); + const round1 = intlSelector(state1); + expect(round1.messages.test).to.not.be.undefined; + expect(round1.messages.alternate).to.be.undefined; + + let state2 = getAlternateState(); + state2.intl = fromJS(state2.intl); + const round2 = intlSelector(state2); + expect(round2.messages.test).to.not.be.undefined; + expect(round2.messages.alternate).to.be.undefined; + }); + function getState() { return { intl: {