Skip to content

Commit

Permalink
Merge pull request #1247 from gaearon/may-fixes
Browse files Browse the repository at this point in the history
May fixes
  • Loading branch information
theKashey authored May 16, 2019
2 parents 875e24a + 8285fb7 commit 701d1a3
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 69 deletions.
4 changes: 3 additions & 1 deletion examples/parcel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"version": "1.0.0",
"license": "MIT",
"scripts": {
"start": "parcel index.html"
"start": "parcel index.html",
"start:prod": "parcel build index.html",
"start:link": "cp -R ../../dist ./node_modules/react-hot-loader && cp -R ../../*.js ./node_modules/react-hot-loader"
},
"devDependencies": {
"babel-core": "^6.26.3",
Expand Down
10 changes: 10 additions & 0 deletions examples/styled-components/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ const Memo2 = React.memo(
},
);

const Memo3 = React.memo(
React.forwardRef(() => (
<div>
[double memo 3 <OtherComponent />
<Counter /> memo]
</div>
)),
);

const TwinComponents = [
({ children }) => <div data-twin="1">{children}</div>,
({ children }) => <div data-twin="2">{children}</div>,
Expand Down Expand Up @@ -96,6 +105,7 @@ const InApp = () => (
<Memo1 a1 a2 />
</Context.Provider>
<Memo2 a1 a2 />
<Memo3 a1 a2 />
<div>
<React.Suspense fallback="loading">
<Async />
Expand Down
10 changes: 7 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
'use strict';

var hasWindow = typeof window !== 'undefined';

if (!module.hot || process.env.NODE_ENV === 'production' || !hasWindow) {
if (process.env.NODE_ENV === 'production') {
module.exports = require('./dist/react-hot-loader.production.min.js');
} else if (!module.hot) {
console.error('React-Hot-Loader: Hot Module Replacement is not enabled');
module.exports = require('./dist/react-hot-loader.production.min.js');
} else if (typeof window === 'undefined') {
// this is just server environment
module.exports = require('./dist/react-hot-loader.production.min.js');
} else {
var evalAllowed = false;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"reload"
],
"devDependencies": {
"@hot-loader/react-dom": "^16.8.6",
"@types/react": "^16.7.13",
"babel-cli": "^6.7.5",
"babel-core": "^6.26.3",
Expand Down
13 changes: 9 additions & 4 deletions root.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@ if (module.hot) {
var hot = require('./index').hot;
var cache = require.cache;

if (!module.parents || !module.parents[0]) {
if (!module.parents || module.parents.length === 0) {
throw new Error(
'React-Hot-Loader: `react-hot-loader/root` is not supported on your system. ' +
'Please use `import {hot} from "react-hot-loader"` instead',
);
}
// access parent
var parent = cache[module.parents[0]];
// remove itself from a cache
if (!parent) {
throw new Error(
'React-Hot-Loader: `react-hot-loader/root` is not supported on your system. ' +
'Please use `import {hot} from "react-hot-loader"` instead',
);
}
// remove self from a cache
delete cache[module.id];
// setup hot for caller

exports.hot = hot(Object.assign({ id: parent.i }, parent));
exports.hot = hot(parent);
} else {
// prod mode
exports.hot = function(a) {
Expand Down
20 changes: 14 additions & 6 deletions src/AppContainer.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ class AppContainer extends React.Component {

static reactHotLoadable = false;

state = {
error: null,
errorInfo: null,
// eslint-disable-next-line react/no-unused-state
generation: 0,
};
constructor(props) {
super(props);
if (configuration.showReactDomPatchNotification) {
configuration.showReactDomPatchNotification = false;
console.warn('React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work.');
}

this.state = {
error: null,
errorInfo: null,
// eslint-disable-next-line react/no-unused-state
generation: 0,
};
}

shouldComponentUpdate(prevProps, prevState) {
// Don't update the component if the state had an error and still has one.
Expand Down
3 changes: 3 additions & 0 deletions src/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const configuration = {
// Disable "hot-replacement-render" when injection into react-dom is made
disableHotRendererWhenInjected: false,

// Controls `react-🔥-dom patch` notification
showReactDomPatchNotification: true,

// Hook on babel component register.
onComponentRegister: false,

Expand Down
21 changes: 12 additions & 9 deletions src/hot.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ const createHoc = (SourceComponent, TargetComponent) => {
return TargetComponent;
};

const makeHotExport = sourceModule => {
const makeHotExport = (sourceModule, moduleId) => {
const updateInstances = possibleError => {
if (possibleError && possibleError instanceof Error) {
console.error(possibleError);
return;
}
const module = hotModule(sourceModule.id);
const module = hotModule(moduleId);
clearTimeout(module.updateTimeout);
module.updateTimeout = setTimeout(() => {
try {
requireIndirect(sourceModule.id);
requireIndirect(moduleId);
} catch (e) {
console.error('React-Hot-Loader: error detected while loading', sourceModule.id);
console.error('React-Hot-Loader: error detected while loading', moduleId);
console.error(e);
}
module.instances.forEach(inst => inst.forceUpdate());
Expand All @@ -68,16 +68,19 @@ const makeHotExport = sourceModule => {
};

const hot = sourceModule => {
if (!sourceModule || !sourceModule.id) {
if (!sourceModule || !sourceModule.hot) {
// this is fatal
throw new Error('React-hot-loader: `hot` could not find the `id` property in the `module` you have provided');
throw new Error('React-hot-loader: `hot` could not find the `hot` method in the `module` you have provided');
}
const moduleId = sourceModule.id || sourceModule.i || sourceModule.filename;
if (!moduleId) {
throw new Error('React-hot-loader: `hot` could not find the `name` of the the `module` you have provided');
}
const moduleId = sourceModule.id;
const module = hotModule(moduleId);
makeHotExport(sourceModule);
makeHotExport(sourceModule, moduleId);

clearExceptions();
const failbackTimer = chargeFailbackTimer(sourceModule.id);
const failbackTimer = chargeFailbackTimer(moduleId);
let firstHotRegistered = false;

// TODO: Ensure that all exports from this file are react components.
Expand Down
5 changes: 5 additions & 0 deletions src/internal/getReactStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ const markUpdate = ({ fiber }) => {
...fiber.memoizedProps,
};
}

if (fiber.stateNode) {
// TODO: this might work better React 16, but breaking tests for React 15 changing "updates" counts.
// updateInstance(fiber.stateNode);
}
};

export const cleanupReact = () => {
Expand Down
5 changes: 2 additions & 3 deletions src/reactHotLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,8 @@ const reactHotLoader = {
configuration.ignoreSFC = configuration.ignoreSFCWhenInjected;

reactHotLoader.IS_REACT_MERGE_ENABLED = true;
} else {
// Actually everything works...
console.warn('React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work.');
configuration.showReactDomPatchNotification = false;
// console.warn('react-🔥-loader activated.');
}
/* eslint-enable */
if (!React.createElement.isPatchedByReactHotLoader) {
Expand Down
2 changes: 1 addition & 1 deletion src/reconciler/componentComparator.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const compareComponents = (oldType, newType, setNewType, baseType) => {
if (oldType.type === newType.type || areSwappable(oldType.type, newType.type)) {
if (baseType) {
// memo form different fibers, why?
if (oldType === baseType) {
if (baseType.$$typeof === newType.$$typeof) {
setNewType(newType);
} else {
setNewType(newType.type);
Expand Down
2 changes: 1 addition & 1 deletion src/webpack/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function transform(source) {
}
if (source.indexOf(sign) >= 0) {
// already patched
return;
return source;
}
for (const key in injectionStart) {
if (source.indexOf(injectionStart[key][0]) > 0 && source.indexOf(injectionEnd[key][0]) > 0) {
Expand Down
13 changes: 10 additions & 3 deletions src/webpack/webpackTagCommonJSExports.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/* eslint-disable global-require, import/no-unresolved */
/* global __FILENAME__ */
/* eslint-disable global-require, import/no-unresolved, no-var, camelcase, func-names */
/* global __FILENAME__, reactHotLoaderGlobal */

(function register() {
// eslint-disable-line no-extra-semi
/* react-hot-loader/webpack */
const reactHotLoader = require('react-hot-loader').default;
var safe_require = function() {
return typeof require === 'undefined' ? undefined : require('react-hot-loader');
};

var reactHotLoader = (typeof reactHotLoaderGlobal !== 'undefined' ? reactHotLoaderGlobal : safe_require()).default;

if (!reactHotLoader) {
return;
Expand All @@ -13,6 +17,9 @@
/* eslint-disable camelcase, no-undef */
const webpackExports = typeof __webpack_exports__ !== 'undefined' ? __webpack_exports__ : exports;
/* eslint-enable camelcase, no-undef */
if (!webpackExports) {
return;
}

if (typeof webpackExports === 'function') {
reactHotLoader.register(webpackExports, 'module.exports', __FILENAME__);
Expand Down
63 changes: 63 additions & 0 deletions test/cases/hooks/useContext.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-env browser */
/* eslint-disable no-lone-blocks, global-require */
import 'babel-polyfill';
import React from 'react';
import TestRenderer from 'react-test-renderer';
import ReactHotLoader, { AppContainer, setConfig } from '../../../src/index.dev';
import { configureGeneration } from '../../../src/global/generation';

// jest.mock('react-dom', () => require('@hot-loader/react-dom'));

describe(`Hooks: useContext`, () => {
beforeEach(() => {
ReactHotLoader.reset();
setConfig({
ignoreSFC: true,
});
configureGeneration(1, 1);
});

if (React.useContext) {
it('use', () => {
let failed = false;
const context = React.createContext(0);
const Wrapper = () => {
const ctx = React.useContext(context);
if (ctx) {
return `pass ${ctx}`;
}
failed = true;
return 'fail';
};
ReactHotLoader.register(Wrapper, 'wrapper', 'hook-test');

const wrapper = TestRenderer.create(
<AppContainer update>
<context.Provider value={1}>
<Wrapper />
</context.Provider>
</AppContainer>,
);

expect(wrapper.toJSON()).toEqual('pass 1');
expect(failed).toBe(false);

{
ReactHotLoader.register(Wrapper, 'wrapper', 'hook-test');
wrapper.update(
<AppContainer update>
<context.Provider value={2}>
<Wrapper />
</context.Provider>
</AppContainer>,
);
expect(wrapper.toJSON()).toEqual('pass 2');
expect(failed).toBe(false);
}
});
} else {
it('target platform does not support useContext', () => {
expect(true).toBe(true);
});
}
});
73 changes: 73 additions & 0 deletions test/cases/memo/memo.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* eslint-env browser */
import 'babel-polyfill';
import React from 'react';
import TestRenderer from 'react-test-renderer';
import ReactHotLoader, { AppContainer } from '../../../src/index.dev';
import { configureGeneration } from '../../../src/global/generation';

// jest.mock('react-dom', () => require('@hot-loader/react-dom'));

describe(`React.memo`, () => {
beforeEach(() => {
ReactHotLoader.reset();
configureGeneration(1, 1);
});

const snapShot = {
children: ['this is component 2'],
props: {},
type: 'div',
};

if (React.memo) {
it('memo wrapping functional component', () => {
const Memo = React.memo(() => <div>this is component 1</div>);
ReactHotLoader.register(Memo, 'memo', 'memo-test');

const wrapper = TestRenderer.create(
<AppContainer>
<Memo />
</AppContainer>,
);

{
const Memo = React.memo(() => <div>this is component 2</div>);
ReactHotLoader.register(Memo, 'memo', 'memo-test');
wrapper.update(
<AppContainer update>
<Memo test />
</AppContainer>,
);

expect(wrapper.toJSON()).toEqual(snapShot);
}
});

it('memo wrapping forwardRef component', () => {
const Memo = React.memo(React.forwardRef(() => <div>this is component 1</div>));
ReactHotLoader.register(Memo, 'memo', 'memo-test');

const wrapper = TestRenderer.create(
<AppContainer>
<Memo />
</AppContainer>,
);

{
const Memo = React.memo(React.forwardRef(() => <div>this is component 2</div>));
ReactHotLoader.register(Memo, 'memo', 'memo-test');
wrapper.update(
<AppContainer update>
<Memo />
</AppContainer>,
);

expect(wrapper.toJSON()).toEqual(snapShot);
}
});
} else {
it('target platform does not support React.memo', () => {
expect(true).toBe(true);
});
}
});
Loading

0 comments on commit 701d1a3

Please sign in to comment.