Created
November 29, 2018 06:22
-
-
Save karlbright/26a86f61226a15f6130703dc25487f9a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {Component, Fragment} from 'react' | |
import {storiesOf} from '@storybook/react' | |
import sample from 'lodash/sample' | |
const stories = storiesOf('⚛ Context', module) | |
// # New React Context API | |
// New React Context API requires both | |
// React >= 16.3.0 along with ReactDOM >= 16.3.0 | |
// | |
// Context provides a way to pass data through the component tree | |
// without having to pass props down manually at every level. | |
// Creating a context is simple with the new React Context API, | |
// and gives us access to both a consumer and provider. | |
const ctx = React.createContext('https://www.youtube.com/results?search_query=') | |
// We use React.createContext and provide a default value. The default value is | |
// used when a matching context provider is not found above in the tree. | |
stories.add('Using context consumer without a provider', () => ( | |
<ctx.Consumer>{value => <a href={value + 'taylor swift'}>View Taylor Swift on YouTube</a>}</ctx.Consumer> | |
)) | |
// We can use a context provider to provide a different value. The default value | |
// is no longer used if a provider is found. In this exmaple we can see that a provider | |
// can be found, but no value is provided by the provider, therefore the the value | |
// is null. | |
stories.add('Using context consumer and provider without a value', () => ( | |
<ctx.Provider> | |
<ctx.Consumer>{spoon => spoon ? 'Be the spoon 🥄' : 'There is no spoon'}</ctx.Consumer> | |
</ctx.Provider> | |
)) | |
// We can provide a value by passing a `value` prop to the context provider, which | |
// will then be accessible via the consumers below it in the tree. The consumer | |
// gets the value from closest matching provider. | |
stories.add('Using context consumer and provider', () => ( | |
<ctx.Provider value='https://www.google.com/search?q='> | |
<ctx.Consumer>{value => <a href={value + 'taylor swift'}>View Taylor Swift on Google</a>}</ctx.Consumer><br /> | |
<ctx.Provider value='https://duckduckgo.com/?q='> | |
<ctx.Consumer>{value => <a href={value + 'taylor swift'}>View Taylor Swift on DuckDuckGo</a>}</ctx.Consumer> | |
</ctx.Provider> | |
</ctx.Provider> | |
)) | |
// Context consumers can be cumbersome to use by way of the context consumer directly. | |
// While our short examples work well enough, we might want to do something a little more | |
// crazy. This is where we can use `contextTypes`, and encapsulate the context consumer | |
// functionality within a component. | |
class BasicSearcher extends Component { | |
static contextType = ctx | |
state = { term: 'taylor swift' } | |
handleInputChange = (event) => this.setState({ term: event.target.value }) | |
render () { | |
return ( | |
<div> | |
<input value={this.state.term} onChange={this.handleInputChange} /><br /> | |
<a href={this.context + this.state.term}>{this.context + this.state.term}</a> | |
</div> | |
) | |
} | |
} | |
stories.add('Using contextType instead of context.Consumer', () => ( | |
<BasicSearcher /> | |
)) | |
// It's important to remember that context provides a way to pass data through | |
// the component tree without having to pass the props manually. So a heavily | |
// nested component tree may work like this. | |
const T = ({children}) => <div className='s'>{children}</div> | |
const A = ({children}) => <div className='w'>{children}</div> | |
const Y = ({children}) => <div className='i'>{children}</div> | |
const L = ({children}) => <div className='f'>{children}</div> | |
const O = ({children}) => <div className='t'>{children}</div> | |
const R = ({children}) => children | |
stories.add('Avoid prop drilling', () => ( | |
<ctx.Provider value='https://duckduckgo.com/?q='> | |
<T><A><Y><L><O><R><BasicSearcher /></R></O></L></Y></A></T> | |
</ctx.Provider> | |
)) | |
// The value you provide in you context is not limited to a string of course. | |
// This means you can provide many values within context, such as a search prefix | |
// and service name. | |
class AdvancedSearcher extends Component { | |
static contextType = ctx | |
state = { term: 'taylor swift' } | |
handleInputChange = (event) => this.setState({ term: event.target.value }) | |
render () { | |
const {service, prefix} = this.context | |
return ( | |
<div> | |
<input value={this.state.term} onChange={this.handleInputChange} /><br /> | |
<a href={prefix + this.state.term}>Search for {this.state.term} on {service}</a> | |
</div> | |
) | |
} | |
} | |
stories.add('Object as a context value', () => ( | |
<ctx.Provider value={{ prefix: 'https://www.youtube.com/results?search_query=', service: 'YouTube' }}> | |
<AdvancedSearcher /> | |
</ctx.Provider> | |
)) | |
// It's common practice to create your own provider that renders your context provider. This is our final | |
// example, which gets closer to a real world example of context. | |
const SERVICES = [ | |
{ service: 'DuckDuckGo', prefix: 'https://www.duckduckgo.com/?q=' }, | |
{ service: 'Google', prefix: 'https://www.google.com/search?q=' }, | |
{ service: 'YouTube', prefix: 'https://www.youtube.com/results?search_query=' } | |
] | |
class SearchEngineProvider extends Component { | |
state = SERVICES[0] | |
handleChange = (event) => this.setState(SERVICES[event.target.value]) | |
render () { | |
return ( | |
<Fragment> | |
<select onChange={this.handleChange}> | |
{SERVICES.map(({service},i) => <option key={service} value={i}>{service}</option>)} | |
</select> | |
<ctx.Provider value={this.state}>{this.props.children}</ctx.Provider> | |
</Fragment> | |
) | |
} | |
} | |
stories.add('Our very own provider', () => ( | |
<SearchEngineProvider> | |
<T><A><Y><L><O><R><AdvancedSearcher /></R></O></L></Y></A></T> | |
</SearchEngineProvider> | |
)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment