Skip to content

Commit

Permalink
refactor: Replace obsolete react context (yahoo#663)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablopalacios committed Jul 17, 2021
1 parent 2dbf3aa commit 3e7e2a9
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 122 deletions.
23 changes: 6 additions & 17 deletions packages/fluxible-addons-react/src/FluxibleComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,15 @@
* Copyright 2015, Yahoo Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
import { Component, cloneElement } from 'react';
import { func, object, node } from 'prop-types';
import { Component, cloneElement, createElement } from 'react';
import { object, node } from 'prop-types';
import { FluxibleProvider } from './FluxibleContext';

class FluxibleComponent extends Component {
getChildContext() {
return {
getStore: this.props.context.getStore,
executeAction: this.props.context.executeAction
};
}

render() {
return cloneElement(this.props.children, {
context: this.props.context
});
const { children, context } = this.props;
const childrenWithContext = cloneElement(children, { context });
return createElement(FluxibleProvider, { context }, childrenWithContext);
}
}

Expand All @@ -25,9 +19,4 @@ FluxibleComponent.propTypes = {
context: object.isRequired
};

FluxibleComponent.childContextTypes = {
executeAction: func.isRequired,
getStore: func.isRequired
};

export default FluxibleComponent;
39 changes: 39 additions & 0 deletions packages/fluxible-addons-react/src/FluxibleContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Component, createContext, createElement } from 'react';
import { arrayOf, node, object, string } from 'prop-types';

export const FluxibleContext = createContext({
executeAction: () => {},
getStore: () => {},
});

export class FluxibleProvider extends Component {
constructor(props) {
super(props);

const state = {
executeAction: this.props.context.executeAction,
getStore: this.props.context.getStore,
};

this.props.plugins.forEach(plugin => {
state[plugin] = this.props.context[plugin];
});

this.state = state;
}

render() {
const props = { value: this.state };
return createElement(FluxibleContext.Provider, props, this.props.children);
}
}

FluxibleProvider.propTypes = {
children: node.isRequired,
context: object.isRequired,
plugins: arrayOf(string)
};

FluxibleProvider.defaultProps = {
plugins: []
};
11 changes: 3 additions & 8 deletions packages/fluxible-addons-react/src/connectToStores.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
import { Component as ReactComponent, createRef, createElement } from 'react';
import { func } from 'prop-types';
import hoistNonReactStatics from 'hoist-non-react-statics';
import { FluxibleContext } from './FluxibleContext';

/**
* Registers change listeners and retrieves state from stores using the `getStateFromStores`
Expand All @@ -25,11 +25,9 @@ import hoistNonReactStatics from 'hoist-non-react-statics';
* @param {array} stores List of stores to listen for changes
* @param {function} getStateFromStores function that receives all stores and should return
* the full state object. Receives `stores` hash and component `props` as arguments
* @param {Object} [customContextTypes] additional `contextTypes` that could be accessed from your `getStateFromStores`
* function
* @returns {React.Component} or {Function} if using decorator pattern
*/
function connectToStores(Component, stores, getStateFromStores, customContextTypes) {
function connectToStores(Component, stores, getStateFromStores) {
class StoreConnector extends ReactComponent {
constructor(props, context) {
super(props, context);
Expand Down Expand Up @@ -75,10 +73,7 @@ function connectToStores(Component, stores, getStateFromStores, customContextTyp

StoreConnector.displayName = `storeConnector(${Component.displayName || Component.name || 'Component'})`;

StoreConnector.contextTypes = {
getStore: func.isRequired,
...customContextTypes
},
StoreConnector.contextType = FluxibleContext;

StoreConnector.WrappedComponent = Component;

Expand Down
17 changes: 7 additions & 10 deletions packages/fluxible-addons-react/src/createElementWithContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,15 @@ function createElementWithContext(fluxibleContext, props) {
if (!Component) {
throw new Error('A top-level component was not passed to the Fluxible constructor.');
}

const context = fluxibleContext.getComponentContext();

if (Component.displayName && Component.displayName.includes('contextProvider')) {
return createElement(
Component,
{context: fluxibleContext.getComponentContext(), ...props},
);
return createElement(Component, { context, ...props});
}
const componentInstance = createElement(Component, props);
return createElement(
FluxibleComponent,
{context: fluxibleContext.getComponentContext()},
componentInstance
);

const children = createElement(Component, props);
return createElement(FluxibleComponent, { context }, children);
}

export default createElementWithContext;
1 change: 1 addition & 0 deletions packages/fluxible-addons-react/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { default as batchedUpdatePlugin } from './batchedUpdatePlugin';
export { default as connectToStores } from './connectToStores';
export { default as createElementWithContext } from './createElementWithContext';
export { default as provideContext } from './provideContext';
export { FluxibleContext } from './FluxibleContext';
31 changes: 8 additions & 23 deletions packages/fluxible-addons-react/src/provideContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { Component as ReactComponent, createRef, createElement } from 'react';
import { func, object } from 'prop-types';
import hoistNonReactStatics from 'hoist-non-react-statics';
import { FluxibleProvider } from './FluxibleContext';

/**
* Provides context prop to all children as React context
Expand All @@ -16,43 +17,27 @@ import hoistNonReactStatics from 'hoist-non-react-statics';
*
* @method provideContext
* @param {React.Component} [Component] component to wrap
* @param {object} customContextTypes Custom contextTypes to add
* @returns {React.Component} or {Function} if using decorator pattern
* @param {array} [plugins] list of plugins names to inject into the context
* @returns {React.Component}
*/
function provideContext(Component, customContextTypes) {
function provideContext(Component, plugins) {
class ContextProvider extends ReactComponent {
constructor(props) {
super(props);
this.wrappedElementRef = createRef();
}

getChildContext() {
const childContext = {
executeAction: this.props.context.executeAction,
getStore: this.props.context.getStore
};
if (customContextTypes) {
Object.keys(customContextTypes).forEach(key => {
childContext[key] = this.props.context[key];
});
}
return childContext;
}

render() {
const props = (Component.prototype && Component.prototype.isReactComponent)
? {ref: this.wrappedElementRef}
: null;
return createElement(Component, {...this.props, ...props});

const { context } = this.props;
const children = createElement(Component, {...this.props, ...props});
return createElement(FluxibleProvider, { context, plugins }, children);
}
}

ContextProvider.childContextTypes = {
executeAction: func.isRequired,
getStore: func.isRequired,
...customContextTypes
};

ContextProvider.propTypes = {
context: object.isRequired
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
import { JSDOM } from 'jsdom';
import createMockComponentContext from 'fluxible/utils/createMockComponentContext';

import { connectToStores, provideContext } from '../../../';
import { connectToStores, provideContext, FluxibleContext } from '../../../';
import FooStore from '../../fixtures/stores/FooStore';
import BarStore from '../../fixtures/stores/BarStore';

Expand All @@ -35,9 +35,7 @@ describe('fluxible-addons-react', () => {

it('should get the state from the stores', (done) => {
class Component extends React.Component {
static contextTypes = {
executeAction: PropTypes.func.isRequired,
}
static contextType = FluxibleContext

constructor() {
super();
Expand Down
15 changes: 5 additions & 10 deletions packages/fluxible-addons-react/tests/unit/lib/provideContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { renderToString } from 'react-dom/server';
import PropTypes from 'prop-types';
import { JSDOM } from 'jsdom';

import { provideContext } from '../../../';
import { provideContext, FluxibleContext } from '../../../';

describe('fluxible-addons-react', () => {
describe('provideContext', () => {
Expand Down Expand Up @@ -53,30 +53,25 @@ describe('fluxible-addons-react', () => {
});

it('should provide the context with custom types to children', () => {
const plugins = ['foo'];
const context = {
foo: 'bar',
executeAction: function() {},
getStore: function() {}
};

class Component extends React.Component {
static contextTypes = {
foo: PropTypes.string.isRequired,
executeAction: PropTypes.func.isRequired,
getStore: PropTypes.func.isRequired
}
static contextType = FluxibleContext;

render() {
expect(this.context.foo).to.equal(context.foo);
expect(this.context.executeAction).to.equal(context.executeAction);
expect(this.context.getStore).to.equal(context.getStore);
expect(this.context.foo).to.equal(context.foo);
return null;
}
}

const WrappedComponent = provideContext(Component, {
foo: PropTypes.string
});
const WrappedComponent = provideContext(Component, plugins);

renderToString(<WrappedComponent context={context} />);
});
Expand Down
13 changes: 4 additions & 9 deletions packages/fluxible-router/src/lib/createNavLinkComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
'use strict';
var React = require('react');
var PropTypes = require('prop-types');
var { FluxibleContext } = require('fluxible-addons-react');
var RouteStore = require('./RouteStore');
var debug = require('debug')('NavLink');
var navigateAction = require('./navigateAction');
Expand Down Expand Up @@ -255,8 +256,7 @@ class NavLink extends React.Component {
try {
onBeforeUnloadText = window.onbeforeunload();
} catch(error) {
var logWarn = (this.context.logger && this.context.logger.warn) || console.warn;
logWarn('Warning: Call of window.onbeforeunload failed', error);
console.warn('Warning: Call of window.onbeforeunload failed', error);
}
}
var confirmResult = onBeforeUnloadText ? window.confirm(onBeforeUnloadText) : true;
Expand Down Expand Up @@ -291,8 +291,7 @@ class NavLink extends React.Component {
throw new Error('NavLink created with empty or missing href \'' + props.href +
'\'or unresolvable routeName \'' + props.routeName);
} else {
var logError = (this.context.logger && this.context.logger.error) || console.error;
logError('Error: Render NavLink with empty or missing href', props);
console.error('Error: Render NavLink with empty or missing href', props);
}
}

Expand Down Expand Up @@ -361,11 +360,7 @@ class NavLink extends React.Component {
NavLink._isMounted = false;
NavLink.autobind = false;
NavLink.displayName = 'NavLink';
NavLink.contextTypes = {
executeAction: PropTypes.func.isRequired,
getStore: PropTypes.func.isRequired,
logger: PropTypes.object
}
NavLink.contextType = FluxibleContext;
NavLink.propTypes = {
href: PropTypes.string,
stopPropagation: PropTypes.bool,
Expand Down
9 changes: 3 additions & 6 deletions packages/fluxible-router/src/lib/handleHistory.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
var React = require('react');
var PropTypes = require('prop-types');
var debug = require('debug')('FluxibleRouter:handleHistory');
var { FluxibleContext } = require('fluxible-addons-react');
var handleRoute = require('../lib/handleRoute');
var navigateAction = require('../lib/navigateAction');
var History = require('./History');
Expand Down Expand Up @@ -59,10 +60,7 @@ function createComponent(Component, opts) {
inherits(HistoryHandler, React.Component);

HistoryHandler.displayName = 'HistoryHandler';
HistoryHandler.contextTypes = {
executeAction: PropTypes.func.isRequired,
logger: PropTypes.object
};
HistoryHandler.contextType = FluxibleContext;
HistoryHandler.propTypes = {
currentRoute: PropTypes.object,
currentNavigate: PropTypes.object
Expand Down Expand Up @@ -168,8 +166,7 @@ function createComponent(Component, opts) {
try {
onBeforeUnloadText = window.onbeforeunload();
} catch(error) {
var logWarn = (this.context.logger && this.context.logger.warn) || console.warn;
logWarn('Warning: Call of window.onbeforeunload failed', error);
console.warn('Warning: Call of window.onbeforeunload failed', error);
}
}
var confirmResult = onBeforeUnloadText ? window.confirm(onBeforeUnloadText) : true;
Expand Down
6 changes: 2 additions & 4 deletions packages/fluxible-router/src/lib/handleRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
'use strict';
var React = require('react');
var PropTypes = require('prop-types');
var connectToStores = require('fluxible-addons-react').connectToStores;
var { connectToStores, FluxibleContext } = require('fluxible-addons-react');
var hoistNonReactStatics = require('hoist-non-react-statics');
var inherits = require('inherits');

Expand All @@ -19,9 +19,7 @@ function createComponent(Component) {
inherits(RouteHandler, React.Component);

RouteHandler.displayName = 'RouteHandler';
RouteHandler.contextTypes = {
getStore: PropTypes.func.isRequired
};
RouteHandler.contextType = FluxibleContext;
RouteHandler.propTypes = {
currentRoute: PropTypes.object,
currentNavigate: PropTypes.object,
Expand Down
Loading

0 comments on commit 3e7e2a9

Please sign in to comment.