React represents a new API ( context API ) that uses the render props
pattern ( template ). At seminars, meetings and on Twitter, I see that there are many questions about using render props
outside of the render, for example, in event handlers or lifecycle hooks.
A year and a half ago, when I was working on React Router v4, I was particularly interested in solving the problem of "deep updates" just once and for all. I created a library called react-context-emission
(later react-broadcast
) with an API that is conceptually identical to what React presented in its new context API.
// React context emission API const { LocationEmitter, LocationSubscriber } = createContextEmission('location') <LocationEmitter location={value}/> <LocationSubscriber>{({ location }) => (...)}</LocationSubscriber> // React Context API const { Provider, Consumer } = React.createContext() <Provider value={location}/> <Consumer>{value => (...)}</Consumer>
After using this template, I really liked to associate variable values with components through the context and draw properties ( render props ), however I tried my best to get access to contextual values outside of rendering. In my implementation, I used to get them from this.context
everywhere. This was one of the reasons why we returned to using the current (outdated?) contextTypes
API in the React Router.
Solving this problem is not so difficult. It took me a while to understand this. However, once you see the solution, it will seem obvious to you. See for yourself!
It is only necessary ... to transfer the value to the handler:
class Something extends React.Component { handleClick = (event, stuff) => { console.log(stuff); }; render() { return ( <SomeContext.Consumer> {stuff => ( <div> <h1>Cool! {stuff}</h1> <button onClick={event => this.handleClick(event, stuff)}> Click me </button> </div> )} </SomeContext.Consumer> ); } }
In the case of lifecycle hooks
previous template does not work, because we do not call hooks, but React. I suggest three templates that I used, choose which one you like best (the third is my favorite!).
You can access data by creating two components: a container that uses a context and a container that accepts a context as a property.
// (, ) const SomethingContainer = () => ( <SomeContext.Consumer> {stuff => <Something stuff={stuff} />} </SomeContext.Consumer> ); // , stuff prop! Context class Something extends React.Component { componentDidMount() { console.log(this.props.stuff); } render() { return ( <div> <h1>Cool! {this.props.stuff}</h1> </div> ); } }
Perhaps you are used to decorating some components with others (for example, using the HOC - High Order Component ). You can quickly turn the render prop
component into a higher order component. I don’t really like this method, because for implementation it requires significant shuffling in the code and a lot of concepts.
// HOC const withStuff = Comp => props => ( <SomeContext.Consumer> {stuff => <Comp stuff={stuff} />} </SomeContext.Consumer> ); // class SomethingImpl extends React.Component { componentDidMount() { console.log(this.props.stuff); } render() { return ( <div> <h1>Cool! {this.props.stuff}</h1> </div> ); } } // the actual decoration const Something = withStuff(SomethingImpl)
Once I created a component that simply took a function as a property and called a function in componentDidUpdate
. I already did a property called render
and now I have another one called didUpdate
. I realized that it is possible to convert each method of a component class into a component property, and this is how @ reactions / component !
It is very convenient to compose render props
in life cycle hooks without any shuffles in the code:
import Component from '@reactions/component'; const Something = () => ( <SomeContext.Consumer> {stuff => ( <Component didMount={() => console.log(stuff)}> <h1>Cool! {stuff}</h1> </Component> )} </SomeContext.Consumer> );
Typically, properties can be compared in componentDidUpdate
, this also works well:
const Something = () => ( <SomeContext.Consumer> {stuff => ( <Component stuff={stuff} didUpdate={({prevProps, props}) => { console.log(prevProps.stuff === props.stuff); }} > <h1>Cool! {stuff}</h1> </Component> )} </SomeContext.Consumer> );
This is my favorite method. The code can change and move without any inconsistencies, because it does not carry new concepts, the whole structure is organized by components. If you no longer need the methods of life cycles, you do not have to unravel the abstraction, just remove <Component/>
.
So now you have everything you need. Everything is obvious, one has only to see once, but before that everything seemed complicated.
Source: https://habr.com/ru/post/350846/
All Articles