Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hooks implementation #149

Merged
merged 29 commits into from
May 27, 2020

Conversation

tstirrat15
Copy link
Contributor

@tstirrat15 tstirrat15 commented May 10, 2020

Fixes #148

Motivation

This has been requested functionality for a while now and I found the time to get around to it.

Changes

I'll annotate the files, since this diff is pretty noisy.

Notes

I'm not entirely sure that the two-pass SSR render behavior has been preserved. I'd love some help figuring that out in code review.

This should be released as a major version according to semantic versioning rules because peer dependencies are changing. It also ended up being a pretty major rewrite.

Copy link
Contributor Author

@tstirrat15 tstirrat15 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments. Please let me know if they're unclear, or if there's additional explanation desired.

@@ -3,18 +3,19 @@
"env": {
"browser": true
},
"plugins": ["import", "react"],
"plugins": ["import", "react", "react-hooks"],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a separate ESLint plugin for the rules of hooks, which will be nice for anyone working on this library.

import PropTypes from "prop-types";
import invariant from "invariant";
import json2mq from "json2mq";

import MediaQueryListener from "./MediaQueryListener";

const queryType = PropTypes.oneOfType([
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed these down to live with the new location for propTypes.

PropTypes.object,
PropTypes.arrayOf(PropTypes.object.isRequired)
]);
const checkInvariants = ({ query, queries, defaultMatches }) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There wasn't a strong reason to pull this out into a separate function - it mostly just seemed noisy in the constructor.

!(!props.query && !props.queries) || (props.query && props.queries),
'<Media> must be supplied with either "query" or "queries"'
);
const wrapInQueryObject = query => ({ __DEFAULT__: query });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this and unWrapSingleQuery up to the top, since I refactored them as arrow functions. Again, that's more a stylistic preference; if there's a strong reason to leave them as functions I can refactor.

const result = this.queries.reduce(
export const useMedia = ({ query, queries, defaultMatches, targetWindow, onChange }) => {
checkInvariants({ query, queries, defaultMatches });
const activeQueries = useRef([]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This provides a stable location for the media queries to live from render to render. I could have used state for this as well, but I figured that since there isn't logic to change query or queries from render to render, having logic to make activeQueries change from render to render wouldn't have made sense.

Actually, now that I think about it, was it an intentional design decision to not allow for the quer(y/ies) props to change from render to render, e.g. in a componentDidUpdate? I can refactor if that would be desired behavior.

@@ -39,33 +39,32 @@
"@babel/plugin-transform-runtime": "^7.1.0",
"@babel/preset-env": "^7.1.6",
"@babel/preset-react": "^7.0.0",
"@rollup/plugin-babel": "5.0.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm audit complained about rollup-plugin-snapshot-size and upgraded it beyond a version that worked with the current version of rollup, so I upgraded Rollup and all its friends.

import { sizeSnapshot } from 'rollup-plugin-size-snapshot';
import { uglify } from 'rollup-plugin-uglify';
import { terser } from 'rollup-plugin-terser';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uglify became Terser. These are otherwise functionally identical packages.

@@ -18,35 +18,35 @@ function external(id) {
const cjs = [
{
input,
output: { file: `cjs/${pkg.name}.js`, format: 'cjs' },
output: { file: `cjs/${pkg.name}.js`, format: 'cjs', exports: 'named' },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rollup wants you to be explicit about named exports when you're providing both a default and named export. It still complains if you provide both a default and a named export, but we're ignoring that for now.

external,
plugins: [
babel({ exclude: /node_modules/ }),
babel({ exclude: /node_modules/, babelHelpers: 'bundled' }),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new version of the babel plugin uses a different setting for whether to include the babel runtime, so this changed throughout the fiel.

},
"webpack": {
"code": 4345
"code": 3673
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check it out! We're actually getting a smaller package out of this.

},
"dependencies": {
"@babel/runtime": "^7.2.0",
"@babel/runtime": "^7.9.6",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this cause problems? Or will it just ensure that the runtime required by an end-user ends up being at least this recent?

@tstirrat15
Copy link
Contributor Author

I was running into this error:

 FAIL  modules/__tests__/Media-test.js
  ● Test suite failed to run

    Requires Babel "^7.0.0-0", but was loaded with "6.26.3". If you are sure you have a compatible version of @babel/core, it is likely that something in your build process is loading the wrong version. Inspect the stack trace of this error to look for the first entry that doesn't mention "@babel/core" or "babel-core" to see what is calling Babel.

      at throwVersionError (node_modules/@babel/helper-plugin-utils/lib/index.js:65:11)
      at Object.assertVersion (node_modules/@babel/helper-plugin-utils/lib/index.js:13:11)
      at node_modules/@babel/plugin-proposal-export-default-from/lib/index.js:17:7
      at node_modules/@babel/helper-plugin-utils/lib/index.js:19:12
          at Array.map (<anonymous>)

And couldn't figure out what to make of it. I found a bunch of issues that talked about it:

but none of the fixes detailed in those issues seemed to work. I tried swapping yarn for npm on a whim, and it started working for reasons that I can't grok.

Any objections to using Yarn?

@tstirrat15
Copy link
Contributor Author

This should be ready for review now.

@edorivai
Copy link
Collaborator

Good catch on trying yarn. I suppose there is some locked package that imports the wrong babel version for some reason. So I deleted the package-lock.json and re-ran npm i. After that npm test runs successfully, it does show some errors in the console though, I think this is expected. Perhaps you could confirm?

I actually think library authors shouldn't use package-lock.json since users won't be applying our lockfile when importing react-media.

> jest

 PASS  modules/__tests__/Media-ssr-test.js
 PASS  modules/__tests__/Media-test.js
  ● Console

    console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
      Error: Uncaught [Invariant Violation: <Media targetWindow> does not support `matchMedia`.]
          at reportException (/home/edo/work/react-media/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
          at invokeEventListeners (/home/edo/work/react-media/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
          at HTMLUnknownElementImpl._dispatch (/home/edo/work/react-media/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/home/edo/work/react-media/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)

...more truncade stack traces

@edorivai
Copy link
Collaborator

Pinging @mjackson to give him a chance to review this if he has time. If he doesn't jump in we'll release an alpha soon.

@tstirrat15
Copy link
Contributor Author

Ah, yeah. Looks like you're correct about lockfiles: https://dev.to/gajus/stop-using-package-lock-json-or-yarn-lock-3ddi

I'll remove the lockfile from this PR and see how it works.

I'll also do some research on silencing that error. I think it's in that test case that's designed to throw, and there's probably something I need to do in Jest to make sure that the error is fully caught.

@tstirrat15
Copy link
Contributor Author

I've made both of those changes. I think there's some additional refactoring and simplification that could be done, but I'd be okay with leaving that for future work.

@edorivai
Copy link
Collaborator

Hey man, just letting you know I haven't forgotten about this! Life a bit hectic ATM, I'll do my best to squeeze this in ASAP.

@tstirrat15
Copy link
Contributor Author

No worries! You've already been more present and responsive than multiple projects that I've submitted PRs to in recent months. Thank you!

Copy link
Collaborator

@edorivai edorivai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, thank you so much man. There are a two points of improvements that we should add, but I want to merge this in and release an alpha tag anyway. I'll open a new issue (#150) for the improvements:

  1. Add tests for useMedia()
  2. Add documentation for useMedia()
  3. Add types to index.d.ts for useMedia()

@edorivai edorivai merged commit 410b4af into ReactTraining:master May 27, 2020
@edorivai
Copy link
Collaborator

edorivai commented May 27, 2020

Now available at react-media@next 🎉
I've released it as a major version bump, because of the required react version 16 upgrade.

It would be great if some people could test it out on their codebases and report back any issues 🙏

@abenhamdine
Copy link
Contributor

great great news !
So glad to see that and thx again for all your hard work.
We will made some tests in our new application and let you know if there are some issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Add hooks to API
3 participants