question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Consider keeping legacy context API for non-state usages

See original GitHub issue

Do you want to request a feature or report a bug? Feature request / question

According to React docs, there are 2 ways to avoid passing props through many levels:

  1. (New) context API
  2. Composition (Inversion of control)

When using the new context API, a consumer component must know, and explicitly import, a context. This raises a quite big disadvantage comparing to the legacy context API: Such component can’t be reusable with different contexts (unless making a prop only version of this component, and wrapping it with another one that uses the context directly). In fact, it means that a component can’t be “contextual” and reusable (by different contexts) at the same time.

Using composition in many cases feels wrong for solving this, quoting the docs: However, this isn’t the right choice in every case: moving more complexity higher in the tree makes those higher-level components more complicated and forces the lower-level components to be more flexible than you may want.

Example of a component I struggle to understand why it should now import a context:

import * as React from "react"
import * as propsTypes from "prop-types"

export class Link extends React.PureComponent {
  static contextTypes = {
    navTo: propsTypes.any
  }
  
  handleClick = (e) => {
    e.preventDefault()
    const {path} = this.props
    this.context.navTo(path)
  }

  render() {
    const {path, ...props} = this.props
    return <a href={path} onClick={this.handleClick} {...props} />
  }
}

If it was already discussed or answered, I apologize, couldn’t find any related issues.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
gaearoncommented, Aug 28, 2018

To complement this, the explicit nature is intentional. The problem with the implicitness of the old API is that you start running into name clashes between different libraries and different parts of a large app because they share the same namespace. Kind of like with global variables.

2reactions
kobiburnleycommented, Aug 27, 2018

@GBarthos Sure

I will start by mentioning that I still find the implicitly of the legacy API a bit more elegant, but that not crucial for me. After understanding where I was wrong about the new API I realized I can get the same result with it.

I had a wrong conception about where and when to use React.createContext. In short, I always used it nearby the “Providers” (where the actual context data existed), instead of nearby the “Consumers”

In my example above I have 1 “consumer” component, Link, and 1 “provider” component, App. Now, let’s say Link is a separated module, with no dependencies, and App is a top level module that depends on the Link module. At first I done this:

app module:

import * as React from "react"
import {Link} from "./link"

export const {Consumer, Provider} = React.createContext({
  navTo: () => {}
})

export class App extends React.PureComponent {
  service = {
    navTo: (path) => this.setState({path}, () => {
      window.location.hash = path
    })
  }
  
  render() {
    return <Provider value={this.service}>
      <Link path="somewhere" />
    </Provider>
  }
}

link module:

import {Consumer} from "./app"

export class LinkIO {
  handleClick = (e) => {
    e.preventDefault()
    const {path, navTo} = this.props
    navTo(path)
  }

  render() {
    const {path, navTo, ...props} = this.props
    return <a href={path} onClick={this.handleClick} {...props} />
  }
}

export class Link extends React.PureComponent {
  render() {
    const {path, ...props} = this.props
    return <Consumer>
      {({navTo}) => <LinkIO path={path} navTo={navTo} {...props}/>}
    </Consumer>
  }
}

It is clear how I break modularity here (app depends on link and link depends on app), I think what confused me is the name “createContext”, it tricked me to always put it where the actual instance of the context is. It actually does not create a context as its name implies, but utility components to provide and consume contexts.

This is how it should be, just the opposite, link doesn’t depends on app:

import * as React from "react"

export const {Consumer, Provider} = React.createContext({
  navTo: () => {
  }
})

export class LinkIO {
  handleClick = (e) => {
    e.preventDefault()
    const {path, navTo} = this.props
    navTo(path)
  }
  
  render() {
    const {path, navTo, ...props} = this.props
    return <a href={path} onClick={this.handleClick} {...props} />
  }
}

export class Link extends React.PureComponent {
  render() {
    const {path, ...props} = this.props
    return <Consumer>
      {({navTo}) => <LinkIO path={path} navTo={navTo} {...props}/>}
    </Consumer>
  }
}
import * as React from "react"
import {Link, Provider} from "./link"

export class App extends React.PureComponent {
  service = {
    navTo: (path) => this.setState({path}, () => {
      window.location.hash = path
    })
  }

  render() {
    return <Provider value={this.service}>
      <Link path="somewhere" />
    </Provider>
  }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Legacy Context - React
The getChildContext function will be called when the state or props changes. In order to update data in the context, trigger a local...
Read more >
When to (Not) Use React Context API for State? - Jannik Wempe
The React Context API can be used to provide state to multiple components far away in the component tree. But is context always...
Read more >
Why Context Should Not be Used for State Management in ...
The main issue with legacy context was that if a component missed rendering via shouldComponentUpdate, updates to values passed down via  ...
Read more >
React / Reactstrap Warning: Legacy context API has been ...
React strap (at the time of this post) uses the react-transition-group things fade in and out and menus slide up and down.
Read more >
Why React Context is Not a "State Management" Tool (and ...
When should I use Redux instead? ︎ · You have larger amounts of application state that are needed in many places in the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found