Skip to content

Commit

Permalink
refactor(context): Switch to new React context API (#40)
Browse files Browse the repository at this point in the history
Change to using the new React context API that was released with React 16.3

BREAKING CHANGE: This requires you to use React >= 16.3.

Make sure to upgrade your `react` and `react-dom` dependencies to be _at least_ 16.3 or greater!
  • Loading branch information
peternoordijk authored and green-arrow committed Mar 9, 2019
1 parent 9b00fdf commit 5f4d581
Show file tree
Hide file tree
Showing 22 changed files with 681 additions and 499 deletions.
37 changes: 12 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"name": "react-firestore",
"version": "0.0.0-semantically-released",
"description": "React components to fetch data from firestore using render props",
"description":
"React components to fetch data from firestore using render props",
"author": "Andrew Walton",
"license": "MIT",
"homepage": "https://github.com/green-arrow/react-firestore#readme",
Expand All @@ -27,51 +28,37 @@
"precommit": "kcd-scripts precommit",
"prepare": "npm run build"
},
"files": [
"dist",
"preact"
],
"files": ["dist", "preact"],
"eslintConfig": {
"extends": "./node_modules/kcd-scripts/eslint.js",
"rules": {
"max-lines": "off",
"no-eq-null": "off",
"eqeqeq": "off",
"react/jsx-indent": "off",
"complexity": [
"error",
12
]
"complexity": ["error", 12]
}
},
"eslintIgnore": [
"node_modules",
"coverage",
"dist"
],
"keywords": [
"react",
"firestore",
"firebase"
],
"eslintIgnore": ["node_modules", "coverage", "dist"],
"keywords": ["react", "firestore", "firebase"],
"dependencies": {
"hoist-non-react-statics": "^2.3.1"
},
"peerDependencies": {
"prop-types": ">=15",
"react": ">=15"
"react": ">=16.3"
},
"devDependencies": {
"commitizen": "^2.9.6",
"cz-conventional-changelog": "^2.1.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme": "^3.8.0",
"enzyme-adapter-react-16": "^1.9.1",
"kcd-scripts": "^0.32.1",
"preact": "^8.2.7",
"prop-types": "^15.6.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0"
"react": "^16.8.1",
"react-dom": "^16.8.1",
"react-test-renderer": "^16.8.1"
},
"config": {
"commitizen": {
Expand Down
26 changes: 10 additions & 16 deletions src/Firestore.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { FirestoreContext } from './FirestoreProvider';

class Firestore extends Component {
static propTypes = {
render: PropTypes.func.isRequired,
};
const Firestore = ({ render }) => (
<FirestoreContext.Consumer>
{({ firestoreDatabase }) => render({ firestore: firestoreDatabase })}
</FirestoreContext.Consumer>
);

static contextTypes = {
firestoreDatabase: PropTypes.object.isRequired,
};

render() {
const { firestoreDatabase } = this.context;
const { render } = this.props;

return render({ firestore: firestoreDatabase });
}
}
Firestore.propTypes = {
render: PropTypes.func.isRequired,
};

export default Firestore;
13 changes: 5 additions & 8 deletions src/FirestoreCollection.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component } from 'react';
import PropTypes from 'prop-types';
import withFirestore from './withFirestore';
import deepEqual from './utils/deepEqual';

class FirestoreCollection extends Component {
Expand All @@ -19,11 +20,7 @@ class FirestoreCollection extends Component {
]),
children: PropTypes.func,
render: PropTypes.func,
};

static contextTypes = {
firestoreDatabase: PropTypes.object.isRequired,
firestoreCache: PropTypes.object.isRequired,
firestore: PropTypes.object.isRequired,
};

state = {
Expand Down Expand Up @@ -63,9 +60,9 @@ class FirestoreCollection extends Component {
}

setupFirestoreListener = props => {
const { firestoreDatabase } = this.context;
const { firestore } = props;
const { path, ...queryProps } = props;
const collectionRef = firestoreDatabase.collection(path);
const collectionRef = firestore.collection(path);
const query = this.buildQuery(collectionRef, queryProps);

this.unsubscribe = query.onSnapshot(
Expand Down Expand Up @@ -139,4 +136,4 @@ class FirestoreCollection extends Component {
}
}

export default FirestoreCollection;
export default withFirestore(FirestoreCollection);
13 changes: 5 additions & 8 deletions src/FirestoreDocument.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { Component } from 'react';
import PropTypes from 'prop-types';
import withFirestore from './withFirestore';

class FirestoreDocument extends Component {
static propTypes = {
path: PropTypes.string.isRequired,
children: PropTypes.func,
render: PropTypes.func,
};

static contextTypes = {
firestoreDatabase: PropTypes.object.isRequired,
firestoreCache: PropTypes.object.isRequired,
firestore: PropTypes.object.isRequired,
};

state = {
Expand Down Expand Up @@ -45,9 +42,9 @@ class FirestoreDocument extends Component {
}

setupFirestoreListener = props => {
const { firestoreDatabase } = this.context;
const { firestore } = props;
const { path } = props;
const documentRef = firestoreDatabase.doc(path);
const documentRef = firestore.doc(path);

this.unsubscribe = documentRef.onSnapshot(
this.handleOnSnapshotSuccess,
Expand Down Expand Up @@ -98,4 +95,4 @@ class FirestoreDocument extends Component {
}
}

export default FirestoreDocument;
export default withFirestore(FirestoreDocument);
23 changes: 9 additions & 14 deletions src/FirestoreProvider.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import { Component } from 'react';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import FirestoreCache from './FirestoreCache';

export const FirestoreContext = React.createContext(null);

export default class FirestoreProvider extends Component {
static propTypes = {
firebase: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
useTimestampsInSnapshots: PropTypes.bool.isRequired,
useTimestampsInSnapshots: PropTypes.bool,
};

static defaultProps = {
useTimestampsInSnapshots: false,
};

static childContextTypes = {
firestoreDatabase: PropTypes.object.isRequired,
firestoreCache: PropTypes.object.isRequired,
};

constructor(props) {
super(props);

Expand All @@ -32,13 +29,11 @@ export default class FirestoreProvider extends Component {
};
}

getChildContext() {
const { firestoreDatabase, firestoreCache } = this.state;

return { firestoreDatabase, firestoreCache };
}

render() {
return this.props.children;
return (
<FirestoreContext.Provider value={this.state}>
{this.props.children}
</FirestoreContext.Provider>
);
}
}
60 changes: 32 additions & 28 deletions src/__tests__/collection.filter.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import { mount } from 'enzyme';
import { FirestoreCollection } from '../';
import { FirestoreCollection, FirestoreProvider } from '../';
import { createMocksForCollection } from './helpers/firestore-utils';

test('filters the documents returned with a simple filter', () => {
const {
firestoreMock,
firebaseMock,
collectionMock,
query,
onSnapshotMock,
Expand All @@ -15,12 +15,13 @@ test('filters the documents returned with a simple filter', () => {
const filter = ['name', '==', 'Mike'];

mount(
<FirestoreCollection
path={collectionName}
filter={filter}
render={renderMock}
/>,
{ context: { firestoreDatabase: firestoreMock, firestoreCache: {} } },
<FirestoreProvider firebase={firebaseMock}>
<FirestoreCollection
path={collectionName}
filter={filter}
render={renderMock}
/>
</FirestoreProvider>,
);

expect(collectionMock).toHaveBeenCalledTimes(1);
Expand All @@ -39,7 +40,7 @@ test('filters the documents returned with a simple filter', () => {

test('filters the documents returned with a compound filter', () => {
const {
firestoreMock,
firebaseMock,
collectionMock,
query,
onSnapshotMock,
Expand All @@ -49,12 +50,13 @@ test('filters the documents returned with a compound filter', () => {
const filter = [['firstName', '==', 'Mike'], ['lastName', '==', 'Smith']];

mount(
<FirestoreCollection
path={collectionName}
filter={filter}
render={renderMock}
/>,
{ context: { firestoreDatabase: firestoreMock, firestoreCache: {} } },
<FirestoreProvider firebase={firebaseMock}>
<FirestoreCollection
path={collectionName}
filter={filter}
render={renderMock}
/>
</FirestoreProvider>,
);

expect(collectionMock).toHaveBeenCalledTimes(1);
Expand All @@ -73,23 +75,25 @@ test('filters the documents returned with a compound filter', () => {
});

test('filter accepts objects and numbers', () => {
const { firestoreMock } = createMocksForCollection();
const { firebaseMock, firestoreMock } = createMocksForCollection();
const renderMock = jest.fn().mockReturnValue(<div />);
const collectionName = 'users';
mount(
<FirestoreCollection
path={collectionName}
filter={['number', '==', 5]}
render={renderMock}
/>,
{ context: { firestoreDatabase: firestoreMock, firestoreCache: {} } },
<FirestoreProvider firebase={firebaseMock}>
<FirestoreCollection
path={collectionName}
filter={['number', '==', 5]}
render={renderMock}
/>
</FirestoreProvider>,
);
mount(
<FirestoreCollection
path={collectionName}
filter={['ref', '==', firestoreMock.doc('things/foobar')]}
render={renderMock}
/>,
{ context: { firestoreDatabase: firestoreMock, firestoreCache: {} } },
<FirestoreProvider firebase={firebaseMock}>
<FirestoreCollection
path={collectionName}
filter={['ref', '==', firestoreMock.doc('things/foobar')]}
render={renderMock}
/>
</FirestoreProvider>,
);
});
12 changes: 7 additions & 5 deletions src/__tests__/collection.firestore-error.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import React from 'react';
import { mount } from 'enzyme';
import { FirestoreCollection } from '../';
import { FirestoreCollection, FirestoreProvider } from '../';
import { createMocksForCollection } from './helpers/firestore-utils';

test('should handle error getting snapshot', () => {
const options = { onSnapshotMockError: true };
const {
firestoreMock,
firebaseMock,
collectionMock,
onSnapshotMock,
} = createMocksForCollection(null, options);
const renderMock = jest.fn().mockReturnValue(<div />);
const collectionName = 'error';

const wrapper = mount(
<FirestoreCollection path={collectionName} render={renderMock} />,
{ context: { firestoreDatabase: firestoreMock, firestoreCache: {} } },
<FirestoreProvider firebase={firebaseMock}>
<FirestoreCollection path={collectionName} render={renderMock} />
</FirestoreProvider>,
);
const component = wrapper.find(FirestoreCollection).children(0);

expect(collectionMock).toHaveBeenCalledTimes(1);
expect(collectionMock).toHaveBeenCalledWith(collectionName);
Expand All @@ -30,5 +32,5 @@ test('should handle error getting snapshot', () => {
snapshot: null,
}),
);
expect(wrapper.state('error')).not.toBe(null);
expect(component.state('error')).not.toBe(null);
});

0 comments on commit 5f4d581

Please sign in to comment.