|
| 1 | +<html> |
| 2 | +<head> |
| 3 | + <title>Render callback | React Patterns & Techniques</title> |
| 4 | + <meta charset="UTF-8"> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> |
| 6 | + <meta name="description" content="Render callback"> |
| 7 | + <meta name="keywords" content="react-patterns, patterns, react, javascript, react-native, reactjs, component, components, front-end, frontend, front-end-development, frontend-web, frontend-webdevelopment, frontend-components, frontend-app, react-app, react-component, react-components, react-techniques, reactjs-patterns"> |
| 8 | + <meta name=" Code Facebook" content=" [email protected]" > |
| 9 | + <link href="css/styles.css" rel="stylesheet"> |
| 10 | +</head> |
| 11 | +<body> |
| 12 | + <div id="container"> |
| 13 | + <header> |
| 14 | + <h1> |
| 15 | + <a href="/">React Patterns & Techniques</a> |
| 16 | + </h1> |
| 17 | + </header> |
| 18 | + <h3>Table of Contents</h3> |
| 19 | + <ul> |
| 20 | + <ul> |
| 21 | + <li><a href="async-initialization-in-componentdidmount.html">Async initialization in componentDidMount()</a></li> |
| 22 | + <li><a href="functional-setstate-(pass-a-function-to-setstate).html">Functional setState (Pass a function to setState)</a></li> |
| 23 | + <li><a href="higher-order-function.html">Higher order function</a></li> |
| 24 | + <li><a href="higher-order-component.html">Higher Order Component - Props proxy</a></li> |
| 25 | + <li><a href="accessing-a-child-component.html">Accessing a child component</a></li> |
| 26 | + <li><a href="jsx-spread-attributes.html">JSX spread attributes</a></li> |
| 27 | + <li><a href="render-callback.html">Render callback</a></li> |
| 28 | + <li><a href="container-component.html">Container component</a></li> |
| 29 | + </ul> |
| 30 | + </ul> |
| 31 | + <h3>Render callback</h3> |
| 32 | + <p> |
| 33 | + Take a look at the example below. Notice that we create a function <code>foo</code> which takes a callback function as a parameter. When we call <code>foo</code>, it turns around and "calls back" to the passed-in function. |
| 34 | + </p> |
| 35 | + <pre class="screen"> |
| 36 | +const foo = (hello) => { |
| 37 | + return hello('foo'); |
| 38 | +}; |
| 39 | + |
| 40 | +foo((name) => { |
| 41 | + return `hello from ${name}`; |
| 42 | +}); |
| 43 | + |
| 44 | +// hello from foo |
| 45 | + </pre> |
| 46 | + <p> |
| 47 | + As you can see, <code>foo</code> used the callback function to complete a portion of a string. In the React world, a render callback works the same way, but returning a portion of the rendered markup. |
| 48 | + </p> |
| 49 | + <p> |
| 50 | + Here’s what we would like to use it: |
| 51 | + </p> |
| 52 | + <pre class="screen"> |
| 53 | +const App = () => { |
| 54 | + return ( |
| 55 | + <div> |
| 56 | + <FieldItem username='Bunlong'> |
| 57 | + {user => user === null ? <Loading /> : <Profile info={user} />} |
| 58 | + </FieldItem> |
| 59 | + </div> |
| 60 | + ); |
| 61 | +}; |
| 62 | + </pre> |
| 63 | + <p> |
| 64 | + <code>FieldItem</code> will render either the <code>Loading</code> or the <code>Profile</code> component, depending on the existence of a <code>user</code> property. It also passes down a prop of its own, <code>username</code>, that one of these components can consume to make a call. |
| 65 | + </p> |
| 66 | + <p> |
| 67 | + What is interesting here is that <code><FieldItem/></code> uses a function as a child. Any child component inside it is now free to consume this prop however it needs to, totally decoupled from the parent. |
| 68 | + </p> |
| 69 | + <p> |
| 70 | + To make it work, the key is to treat <code>this.props.children</code> as a function. So in order for the <code>Profile</code> component to render whatever it needs to render, it needs to run the callback on the <code>children</code> function, passing it the <code>user</code> argument it expects. Here’s an example implementation of <code>Profile</code>: |
| 71 | + </p> |
| 72 | + <pre class="screen"> |
| 73 | +class FieldItem extends React.Component { |
| 74 | + state = { user: null } |
| 75 | + |
| 76 | + componentDidMount() { |
| 77 | + // We can make an ajax call here, for e.g. |
| 78 | + setTimeout(() => this.setState({ |
| 79 | + user: `I have now fulfilled something for ${this.props.username}` |
| 80 | + }), 1500); |
| 81 | + } |
| 82 | + |
| 83 | + render() { |
| 84 | + // Render the children with a function using state as the argument |
| 85 | + return this.props.children(this.state.user); |
| 86 | + } |
| 87 | +} |
| 88 | + </pre> |
| 89 | + <p> |
| 90 | + The key there is the child component rendering <code>return this.props.children(this.state.user)</code> with its own state. This means its up to the component to decide how to use the arguments it receives, and the parent component <code>FieldItem</code> doesn’t care: it only manages which component to render, in this case. |
| 91 | + </p> |
| 92 | + <p> |
| 93 | + Looking at <code>Profile</code>, since <code>user</code> is <code>null</code> for 1000ms, the callback receives null as a value for user, thus rendering <code><Loading /></code> first. Once we have a <code>user</code>, the Profile component will then render. I really enjoy the simplicity and the cleanliness of this approach managing components. |
| 94 | + </p> |
| 95 | + <pre class="screen"> |
| 96 | +// Loading component |
| 97 | +const Loading = () => <p>Loading...</p>; |
| 98 | + |
| 99 | +// Profile component |
| 100 | +const Profile = (props) => <p>{props.info}</p>; |
| 101 | + |
| 102 | +const App = () => { |
| 103 | + return ( |
| 104 | + <div> |
| 105 | + <h3>An application</h3> |
| 106 | + <FieldItem username='magalhini'> |
| 107 | + { user => user === null ? <Loading /> : <Profile info={user} /> } |
| 108 | + </FieldItem> |
| 109 | + </div> |
| 110 | + ); |
| 111 | +}; |
| 112 | + |
| 113 | +class FieldItem extends React.Component { |
| 114 | + state = { user: null } |
| 115 | + |
| 116 | + componentDidMount() { |
| 117 | + // We can make an ajax call here, for e.g. |
| 118 | + setTimeout(() => this.setState({ |
| 119 | + user: `I have now fulfilled something for ${this.props.username}` |
| 120 | + }), 1500); |
| 121 | + } |
| 122 | + |
| 123 | + render() { |
| 124 | + // Render the children with a function using state as the argument |
| 125 | + return this.props.children(this.state.user); |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +ReactDOM.render(<App/>, document.getElementById('app')); |
| 130 | + </pre> |
| 131 | + </div> |
| 132 | +</body> |
| 133 | +</html> |
0 commit comments