diff --git a/modules/BrowserHistory.js b/modules/BrowserHistory.js index 5af0450..d2eefc1 100644 --- a/modules/BrowserHistory.js +++ b/modules/BrowserHistory.js @@ -1,6 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import createBrowserHistory from "history/createBrowserHistory"; +import { canUseDOM } from "history/DOMUtils"; import { history as historyType } from "./PropTypes"; /** @@ -24,23 +25,29 @@ class BrowserHistory extends React.Component { return { history: this.history }; } - componentWillMount() { - const { - basename, - forceRefresh, - getUserConfirmation, - keyLength - } = this.props; - - this.history = createBrowserHistory({ - basename, - forceRefresh, - getUserConfirmation, - keyLength - }); - - // Do this here so we catch actions in cDM. - this.unlisten = this.history.listen(() => this.forceUpdate()); + constructor(props) { + super(props); + + if (canUseDOM) { + const { + basename, + forceRefresh, + getUserConfirmation, + keyLength + } = this.props; + + this.history = createBrowserHistory({ + basename, + forceRefresh, + getUserConfirmation, + keyLength + }); + + // Do this here so we catch actions in cDM. + this.unlisten = this.history.listen(() => this.forceUpdate()); + } else { + this.history = {}; + } } componentWillUnmount() { diff --git a/modules/HashHistory.js b/modules/HashHistory.js index 80b882a..9c54d84 100644 --- a/modules/HashHistory.js +++ b/modules/HashHistory.js @@ -1,6 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import createHashHistory from "history/createHashHistory"; +import { canUseDOM } from "history/DOMUtils"; import { history as historyType } from "./PropTypes"; /** @@ -22,17 +23,23 @@ class HashHistory extends React.Component { return { history: this.history }; } - componentWillMount() { + constructor(props) { + super(props); + const { basename, getUserConfirmation, hashType } = this.props; - this.history = createHashHistory({ - basename, - getUserConfirmation, - hashType - }); - - // Do this here so we catch actions in cDM. - this.unlisten = this.history.listen(() => this.forceUpdate()); + if (canUseDOM) { + this.history = createHashHistory({ + basename, + getUserConfirmation, + hashType + }); + + // Do this here so we catch actions in cDM. + this.unlisten = this.history.listen(() => this.forceUpdate()); + } else { + this.history = {}; + } } componentWillUnmount() { diff --git a/modules/MemoryHistory.js b/modules/MemoryHistory.js index 88e7d64..d8610d6 100644 --- a/modules/MemoryHistory.js +++ b/modules/MemoryHistory.js @@ -1,6 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import createMemoryHistory from "history/createMemoryHistory"; +import { canUseDOM } from "history/DOMUtils"; import { history as historyType } from "./PropTypes"; /** @@ -23,23 +24,29 @@ class MemoryHistory extends React.Component { return { history: this.history }; } - componentWillMount() { - const { - getUserConfirmation, - initialEntries, - initialIndex, - keyLength - } = this.props; - - this.history = createMemoryHistory({ - getUserConfirmation, - initialEntries, - initialIndex, - keyLength - }); - - // Do this here so we catch actions in cDM. - this.unlisten = this.history.listen(() => this.forceUpdate()); + constructor(props) { + super(props); + + if (canUseDOM) { + const { + getUserConfirmation, + initialEntries, + initialIndex, + keyLength + } = this.props; + + this.history = createMemoryHistory({ + getUserConfirmation, + initialEntries, + initialIndex, + keyLength + }); + + // Do this here so we catch actions in cDM. + this.unlisten = this.history.listen(() => this.forceUpdate()); + } else { + this.history = {}; + } } componentWillUnmount() { diff --git a/modules/Prompt.js b/modules/Prompt.js index 8de3c12..1d41e72 100644 --- a/modules/Prompt.js +++ b/modules/Prompt.js @@ -1,5 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; +import { polyfill } from 'react-lifecycles-compat'; +import { canUseDOM } from "history/DOMUtils"; import { history as historyType } from "./PropTypes"; class Prompt extends React.Component { @@ -16,9 +18,18 @@ class Prompt extends React.Component { when: true }; + constructor(props, context) { + super(props, context); + + if (canUseDOM) { + if (this.props.when) { + this.enable(this.props.message); + } + } + } + enable(message) { if (this.unblock) this.unblock(); - this.unblock = this.context.history.block(message); } @@ -29,17 +40,18 @@ class Prompt extends React.Component { } } - componentWillMount() { - if (this.props.when) this.enable(this.props.message); - } - - componentWillReceiveProps(nextProps) { - if (nextProps.when) { - if (!this.props.when || this.props.message !== nextProps.message) - this.enable(nextProps.message); + getSnapshotBeforeUpdate(prevProps) { + if (this.props.when) { + if (!prevProps.when || prevProps.message !== this.props.message) + this.enable(this.props.message); } else { this.disable(); } + return null; + } + + componentDidUpdate() { + // we must define this lifecycle method as long as we're using the polyfill } componentWillUnmount() { @@ -51,4 +63,7 @@ class Prompt extends React.Component { } } +// Polyfill your component so the new lifecycles will work with older versions of React: +polyfill(Prompt); + export default Prompt; diff --git a/modules/__tests__/BrowserHistory-test.js b/modules/__tests__/BrowserHistory-test.js index 191680f..359c853 100644 --- a/modules/__tests__/BrowserHistory-test.js +++ b/modules/__tests__/BrowserHistory-test.js @@ -114,6 +114,11 @@ describe('BrowserHistory', () => { const children = RenderTestSequences.PromptBlocksTheForwardButton(done) render(, node) }) + + it('updates the message', (done) => { + const children = RenderTestSequences.PromptUpdates(done) + render(, node) + }) }) describe('inactive prompt', () => { diff --git a/modules/__tests__/HashHistory-test.js b/modules/__tests__/HashHistory-test.js index 6b1ecf6..d3078fb 100644 --- a/modules/__tests__/HashHistory-test.js +++ b/modules/__tests__/HashHistory-test.js @@ -104,6 +104,11 @@ describe('HashHistory', () => { const children = RenderTestSequences.PromptBlocksTheForwardButton(done) render(, node) }) + + it('updates the message', (done) => { + const children = RenderTestSequences.PromptUpdates(done) + render(, node) + }) }) describe('"hashbang" hash encoding', () => { diff --git a/modules/__tests__/MemoryHistory-test.js b/modules/__tests__/MemoryHistory-test.js index 1ea3b10..189086f 100644 --- a/modules/__tests__/MemoryHistory-test.js +++ b/modules/__tests__/MemoryHistory-test.js @@ -90,6 +90,11 @@ describe('MemoryHistory', () => { const children = RenderTestSequences.PromptBlocksTheForwardButton(done) render(, node) }) + + it('updates the message', (done) => { + const children = RenderTestSequences.PromptUpdates(done) + render(, node) + }) }) describe('inactive prompt', () => { diff --git a/modules/__tests__/RenderTestSequences/PromptUpdates.js b/modules/__tests__/RenderTestSequences/PromptUpdates.js new file mode 100644 index 0000000..93b1ef6 --- /dev/null +++ b/modules/__tests__/RenderTestSequences/PromptUpdates.js @@ -0,0 +1,49 @@ +import React from 'react' +import expect, { spyOn } from 'expect' +import { Push } from '../../Actions' +import Prompt from '../../Prompt' +import createRenderProp from './createRenderProp' + +export default (done) => { + class TestComponent extends React.Component { + state = { message: 'Are you sure?', when: false } + componentDidMount() { + this.setState({ message: 'Are you totally sure?', when: true }) + } + render() { + return ( +
+ + {this.state.when && } +
+ ) + } + } + + const steps = [ + (history) => { + const { action, location } = history + expect(action).toBe('POP') + expect(location).toMatch({ + pathname: '/' + }) + + spyOn(history, 'block') + + return + }, + ({ block }) => { + expect(block.calls.length).toBe(1) + expect(block).toHaveBeenCalledWith('Are you totally sure?') + expect(location).toMatch({ + pathname: '/hello' + }) + + block.restore() + + return null + } + ] + + return createRenderProp(steps, done) +} diff --git a/modules/__tests__/RenderTestSequences/ReplaceChangesTheKey.js b/modules/__tests__/RenderTestSequences/ReplaceChangesTheKey.js index 73ecb05..22bfd5d 100644 --- a/modules/__tests__/RenderTestSequences/ReplaceChangesTheKey.js +++ b/modules/__tests__/RenderTestSequences/ReplaceChangesTheKey.js @@ -10,7 +10,6 @@ export default (done) => { ({ location }) => { expect(location).toMatch({ pathname: '/', - key: undefined }) return diff --git a/modules/__tests__/RenderTestSequences/index.js b/modules/__tests__/RenderTestSequences/index.js index f4ca854..d8714d3 100644 --- a/modules/__tests__/RenderTestSequences/index.js +++ b/modules/__tests__/RenderTestSequences/index.js @@ -7,6 +7,7 @@ export PromptBlocksAPush from './PromptBlocksAPush' export PromptBlocksAReplace from './PromptBlocksAReplace' export PromptBlocksTheBackButton from './PromptBlocksTheBackButton' export PromptBlocksTheForwardButton from './PromptBlocksTheForwardButton' +export PromptUpdates from './PromptUpdates' export PushNewLocation from './PushNewLocation' export PushAction from './PushAction' export PushWithStateUsesAKey from './PushWithStateUsesAKey' diff --git a/package.json b/package.json index 8ac9f99..fab0436 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ }, "dependencies": { "history": "^4.5.0", - "prop-types": "^15.6.0" + "prop-types": "^15.6.0", + "react-lifecycles-compat": "^3.0.4" }, "peerDependencies": { "react": "15.x" @@ -26,10 +27,10 @@ "babel-cli": "^6.18.0", "babel-eslint": "^7.0.0", "babel-loader": "^6.2.10", - "babel-plugin-transform-react-remove-prop-types": "^0.2.11", - "babel-preset-es2015": "^6.18.0", - "babel-preset-react": "^6.5.0", - "babel-preset-stage-1": "^6.5.0", + "babel-plugin-transform-react-remove-prop-types": "^0.2.12", + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babel-preset-stage-1": "^6.24.1", "eslint": "^3.3.1", "eslint-plugin-import": "^2.0.0", "eslint-plugin-react": "^6.1.2", @@ -47,11 +48,15 @@ "karma-webpack": "^1.7.0", "mocha": "^3.0.2", "pretty-bytes": "^4.0.0", - "react": "^15.3.0", - "react-dom": "^15.3.0", + "react": "^16.4.0", + "react-dom": "^16.4.0", "readline-sync": "^1.4.4", "webpack": "1.13.1", "webpack-dev-server": "1.16.2" }, - "keywords": ["react", "history", "link"] + "keywords": [ + "react", + "history", + "link" + ] }