Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,30 @@ Keys of media query objects are camel-cased and numeric values automatically get

An optional `targetWindow` prop can be specified if you want the `query` to be evaluated against a different window object than the one the code is running in. This can be useful for example if you are rendering part of your component tree to an iframe or [a popup window](https://hackernoon.com/using-a-react-16-portal-to-do-something-cool-2a2d627b0202).

There is also an optional `onChange` prop, which is a callback function that will be invoked when the status of the media query changes. This can be useful if you need to do some imperative stuff in addition to the declarative approach `react-media` already provides.

```jsx
import React from "react";
import Media from "react-media";

class App extends React.Component {
render() {
return (
<div>
<Media
query="(max-width: 599px)"
onChange={matches =>
matches
? alert("The document is less than 600px wide.")
: alert("The document is at least 600px wide.")
}
/>
</div>
);
}
}
```

If you're curious about how react-media differs from [react-responsive](https://github.com/contra/react-responsive), please see [this comment](https://github.com/ReactTraining/react-media/issues/70#issuecomment-347774260).

Enjoy!
Expand Down
22 changes: 18 additions & 4 deletions modules/Media.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class Media extends React.Component {
]).isRequired,
render: PropTypes.func,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
targetWindow: PropTypes.object
targetWindow: PropTypes.object,
onChange: PropTypes.func
};

static defaultProps = {
Expand All @@ -27,7 +28,16 @@ class Media extends React.Component {
matches: this.props.defaultMatches
};

updateMatches = () => this.setState({ matches: this.mediaQueryList.matches });
updateMatches = () => {
const { matches } = this.mediaQueryList;

this.setState({ matches });

const { onChange } = this.props;
if (onChange) {
onChange(matches);
}
};

componentWillMount() {
if (typeof window !== "object") return;
Expand Down Expand Up @@ -56,12 +66,16 @@ class Media extends React.Component {
const { matches } = this.state;

return render
? matches ? render() : null
? matches
? render()
: null
: children
? typeof children === "function"
? children(matches)
: !Array.isArray(children) || children.length // Preact defaults to empty children array
? matches ? React.Children.only(children) : null
? matches
? React.Children.only(children)
: null
: null
: null;
}
Expand Down
15 changes: 15 additions & 0 deletions modules/__tests__/Media-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,21 @@ describe("A <Media>", () => {
});
});

describe("when an onChange function is passed", () => {
beforeEach(() => {
window.matchMedia = createMockMediaMatcher(true);
});

it("calls the function with the match result", () => {
const callback = jest.fn();
const element = <Media query="" onChange={callback} />;

ReactDOM.render(element, node, () => {
expect(callback).toHaveBeenCalledWith(true);
});
});
});

describe("rendered on the server", () => {
beforeEach(() => {
window.matchMedia = createMockMediaMatcher(true);
Expand Down