From 0780f7252ef7c1170b63cf2ff8339f0ad94b58e2 Mon Sep 17 00:00:00 2001 From: Alex Krolick Date: Tue, 20 Mar 2018 18:14:16 -0700 Subject: [PATCH] Reorganize examples & legacy docs --- content/docs/context.md | 215 +++++---------------------------- content/docs/legacy-context.md | 215 +++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+), 187 deletions(-) create mode 100644 content/docs/legacy-context.md diff --git a/content/docs/context.md b/content/docs/context.md index 05af3314..47c0f19a 100644 --- a/content/docs/context.md +++ b/content/docs/context.md @@ -6,15 +6,9 @@ permalink: docs/context.html Context provides a way to pass data through the component tree without having to pass props down manually at every level. -## Using Context +## API -Here is an example illustrating how you might inject a "theme" using context: - -`embed:context/theme-example.js` - -### API - -#### `React.createContext` +### `React.createContext` ```js const {Provider, Consumer} = React.createContext([default]); @@ -25,7 +19,7 @@ Creates a `{ Provider, Consumer }` pair. Takes one argument, the default context that Consumers will receive when they don't have a matching Provider. -#### `Provider` +### `Provider` ```js @@ -35,7 +29,7 @@ A React component that allows Consumers to subscribe to context changes. Takes one prop, `value`, which will be passed to the [render prop](/docs/render-props.html) of child Consumers for the matching context anywhere in the component tree. One Provider can be connected to many Consumers. -#### `Consumer` +### `Consumer` ```js @@ -51,28 +45,32 @@ Takes a function as the `children` prop that receives the `value` prop of the ma > > For more information about this pattern, see [render props](/docs/render-props.html). -### Typical Usage +## Examples -`embed:context/theme-detailed-theme-context.js` +### Static Context -`embed:context/theme-detailed-themed-button.js` +Here is an example illustrating how you might inject a "theme" using context: -`embed:context/theme-detailed-app.js` +`embed:context/theme-example.js` +### Dynamic Context -## Experimental API (Deprecated in React 16.3) +A more complex example with dynamic values for the theme: -> The old experimental API is deprecated as of React 16.3. The API will be supported in all 16.x releases, but applications using it should migrate to the new API. +**theme-contex.js** +`embed:context/theme-detailed-theme-context.js` -The experimental API lacked a safe mechanism to update context. Version 16.3 introduced a new context API that is more efficient and supports both static type checking and deep updates. +**themed-button.js** +`embed:context/theme-detailed-themed-button.js` -### How To Use Context +**app.js** +`embed:context/theme-detailed-app.js` -> This section documents a deprecated API +## Motivation -Suppose you have a structure like: +Context is designed to relieve the pain of passing props down through a deeply nested component tree. For example, in the code below we manually thread through a color prop in order to style the Button and Message components. Using context, we can avoid passing props through intermediate elements. -```javascript +```js class Button extends React.Component { render() { return ( @@ -87,6 +85,11 @@ class Message extends React.Component { render() { return (
+ {/* + The Message component must take `color` as as prop to pass it to the + Button. Using context, the Button could connect to the color context + on its own. + */} {this.props.text}
); @@ -104,170 +107,8 @@ class MessageList extends React.Component { } ``` -In this example, we manually thread through a `color` prop in order to style the `Button` and `Message` components appropriately. Using context, we can pass this through the tree automatically: - -```javascript{6,13-15,21,28-30,40-42} -import PropTypes from 'prop-types'; - -class Button extends React.Component { - render() { - return ( - - ); - } -} - -Button.contextTypes = { - color: PropTypes.string -}; - -class Message extends React.Component { - render() { - return ( -
- {this.props.text} -
- ); - } -} - -class MessageList extends React.Component { - getChildContext() { - return {color: "purple"}; - } - - render() { - const children = this.props.messages.map((message) => - - ); - return
{children}
; - } -} - -MessageList.childContextTypes = { - color: PropTypes.string -}; -``` - -By adding `childContextTypes` and `getChildContext` to `MessageList` (the context provider), React passes the information down automatically and any component in the subtree (in this case, `Button`) can access it by defining `contextTypes`. - -If `contextTypes` is not defined, then `context` will be an empty object. - -> Note: -> -> `React.PropTypes` has moved into a different package since React v15.5. Please use [the `prop-types` library instead](https://www.npmjs.com/package/prop-types) to define `contextTypes`. -> -> We provide [a codemod script](/blog/2017/04/07/react-v15.5.0.html#migrating-from-react.proptypes) to automate the conversion. - -### Parent-Child Coupling - -> This section documents a deprecated API - -Context can also let you build an API where parents and children communicate. For example, one library that works this way is [React Router V4](https://reacttraining.com/react-router): - -```javascript -import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; - -const BasicExample = () => ( - -
-
    -
  • Home
  • -
  • About
  • -
  • Topics
  • -
- -
- - - - -
-
-); -``` - -By passing down some information from the `Router` component, each `Link` and `Route` can communicate back to the containing `Router`. - -Before you build components with an API similar to this, consider if there are cleaner alternatives. For example, you can pass entire React components as props if you'd like to. - -### Referencing Context in Lifecycle Methods - -> This section documents a deprecated API - -If `contextTypes` is defined within a component, the following [lifecycle methods](/docs/react-component.html#the-component-lifecycle) will receive an additional parameter, the `context` object: - -- [`constructor(props, context)`](/docs/react-component.html#constructor) -- [`componentWillReceiveProps(nextProps, nextContext)`](/docs/react-component.html#componentwillreceiveprops) -- [`shouldComponentUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#shouldcomponentupdate) -- [`componentWillUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#componentwillupdate) - -> Note: -> -> As of React 16, `componentDidUpdate` no longer receives `prevContext`. - -### Referencing Context in Stateless Functional Components - -> This section documents a deprecated API - -Stateless functional components are also able to reference `context` if `contextTypes` is defined as a property of the function. The following code shows a `Button` component written as a stateless functional component. - -```javascript -import PropTypes from 'prop-types'; - -const Button = ({children}, context) => - ; - -Button.contextTypes = {color: PropTypes.string}; -``` - -### Updating Context - -> This section documents a deprecated API - -Don't do it. - -React has an API to update context, but it is fundamentally broken and you should not use it. - -The `getChildContext` function will be called when the state or props changes. In order to update data in the context, trigger a local state update with `this.setState`. This will trigger a new context and changes will be received by the children. - -```javascript -import PropTypes from 'prop-types'; - -class MediaQuery extends React.Component { - constructor(props) { - super(props); - this.state = {type:'desktop'}; - } - - getChildContext() { - return {type: this.state.type}; - } - - componentDidMount() { - const checkMediaQuery = () => { - const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile'; - if (type !== this.state.type) { - this.setState({type}); - } - }; - - window.addEventListener('resize', checkMediaQuery); - checkMediaQuery(); - } - - render() { - return this.props.children; - } -} - -MediaQuery.childContextTypes = { - type: PropTypes.string -}; -``` +## Legacy API -The problem is, if a context value provided by component changes, descendants that use that value won't update if an intermediate parent returns `false` from `shouldComponentUpdate`. This is totally out of control of the components using context, so there's basically no way to reliably update the context. [This blog post](https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076) has a good explanation of why this is a problem and how you might get around it. +> The legacy context API was deprecated in React 16.3 +> +> React previously shipped with an experimental context API. The old API will be supported in all 16.x releases, but applications using it should migrate to the new version. Read the [legacy context docs here](/docs/legacy-context.html). diff --git a/content/docs/legacy-context.md b/content/docs/legacy-context.md new file mode 100644 index 00000000..d9e899f0 --- /dev/null +++ b/content/docs/legacy-context.md @@ -0,0 +1,215 @@ +--- +id: legacy-context +title: Legacy Context +permalink: docs/legacy-context.html +--- + +> This API is deprecated as of React 16.3. +> +> The API will be supported in all 16.x releases, but applications using it should migrate to the [new API](/docs/context.html). The experimental API lacked a safe mechanism to update context. Version 16.3 introduced a new context API that is more efficient and supports both static type checking and deep updates. + +## How To Use Context + +> This section documents a deprecated API. See the [new API](/docs/context.html). + +Suppose you have a structure like: + +```javascript +class Button extends React.Component { + render() { + return ( + + ); + } +} + +class Message extends React.Component { + render() { + return ( +
+ {this.props.text} +
+ ); + } +} + +class MessageList extends React.Component { + render() { + const color = "purple"; + const children = this.props.messages.map((message) => + + ); + return
{children}
; + } +} +``` + +In this example, we manually thread through a `color` prop in order to style the `Button` and `Message` components appropriately. Using context, we can pass this through the tree automatically: + +```javascript{6,13-15,21,28-30,40-42} +import PropTypes from 'prop-types'; + +class Button extends React.Component { + render() { + return ( + + ); + } +} + +Button.contextTypes = { + color: PropTypes.string +}; + +class Message extends React.Component { + render() { + return ( +
+ {this.props.text} +
+ ); + } +} + +class MessageList extends React.Component { + getChildContext() { + return {color: "purple"}; + } + + render() { + const children = this.props.messages.map((message) => + + ); + return
{children}
; + } +} + +MessageList.childContextTypes = { + color: PropTypes.string +}; +``` + +By adding `childContextTypes` and `getChildContext` to `MessageList` (the context provider), React passes the information down automatically and any component in the subtree (in this case, `Button`) can access it by defining `contextTypes`. + +If `contextTypes` is not defined, then `context` will be an empty object. + +> Note: +> +> `React.PropTypes` has moved into a different package since React v15.5. Please use [the `prop-types` library instead](https://www.npmjs.com/package/prop-types) to define `contextTypes`. +> +> We provide [a codemod script](/blog/2017/04/07/react-v15.5.0.html#migrating-from-react.proptypes) to automate the conversion. + +### Parent-Child Coupling + +> This section documents a deprecated API. See the [new API](/docs/context.html). + +Context can also let you build an API where parents and children communicate. For example, one library that works this way is [React Router V4](https://reacttraining.com/react-router): + +```javascript +import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; + +const BasicExample = () => ( + +
+
    +
  • Home
  • +
  • About
  • +
  • Topics
  • +
+ +
+ + + + +
+
+); +``` + +By passing down some information from the `Router` component, each `Link` and `Route` can communicate back to the containing `Router`. + +Before you build components with an API similar to this, consider if there are cleaner alternatives. For example, you can pass entire React components as props if you'd like to. + +### Referencing Context in Lifecycle Methods + +> This section documents a deprecated API. See the [new API](/docs/context.html). + +If `contextTypes` is defined within a component, the following [lifecycle methods](/docs/react-component.html#the-component-lifecycle) will receive an additional parameter, the `context` object: + +- [`constructor(props, context)`](/docs/react-component.html#constructor) +- [`componentWillReceiveProps(nextProps, nextContext)`](/docs/react-component.html#componentwillreceiveprops) +- [`shouldComponentUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#shouldcomponentupdate) +- [`componentWillUpdate(nextProps, nextState, nextContext)`](/docs/react-component.html#componentwillupdate) + +> Note: +> +> As of React 16, `componentDidUpdate` no longer receives `prevContext`. + +### Referencing Context in Stateless Functional Components + +> This section documents a deprecated API. See the [new API](/docs/context.html). + +Stateless functional components are also able to reference `context` if `contextTypes` is defined as a property of the function. The following code shows a `Button` component written as a stateless functional component. + +```javascript +import PropTypes from 'prop-types'; + +const Button = ({children}, context) => + ; + +Button.contextTypes = {color: PropTypes.string}; +``` + +### Updating Context + +> This section documents a deprecated API. See the [new API](/docs/context.html). + +Don't do it. + +React has an API to update context, but it is fundamentally broken and you should not use it. + +The `getChildContext` function will be called when the state or props changes. In order to update data in the context, trigger a local state update with `this.setState`. This will trigger a new context and changes will be received by the children. + +```javascript +import PropTypes from 'prop-types'; + +class MediaQuery extends React.Component { + constructor(props) { + super(props); + this.state = {type:'desktop'}; + } + + getChildContext() { + return {type: this.state.type}; + } + + componentDidMount() { + const checkMediaQuery = () => { + const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile'; + if (type !== this.state.type) { + this.setState({type}); + } + }; + + window.addEventListener('resize', checkMediaQuery); + checkMediaQuery(); + } + + render() { + return this.props.children; + } +} + +MediaQuery.childContextTypes = { + type: PropTypes.string +}; +``` + +The problem is, if a context value provided by component changes, descendants that use that value won't update if an intermediate parent returns `false` from `shouldComponentUpdate`. This is totally out of control of the components using context, so there's basically no way to reliably update the context. [This blog post](https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076) has a good explanation of why this is a problem and how you might get around it. \ No newline at end of file