React Render Props簡介。
簡介
React Render Props如字面所述是一個props,但與一般props的差別在於其值為一個返回可渲染物件的函式。
例如下面<Component/>
的render
屬性接收的值data => <h1>Hello, {data}!</h1>
為一個返回React element的函式,所以render
即為一個render props。
<Component render={data => <h1>Hello, {data}!</h1>}>
Render Props只是React程式的一種設計手法而非API,所以上面的render
也可用其他任意名稱。
Render Props用途為把狀態及行為抽離出來為component,讓其他component也可享用相同的特性。簡單說就是提高重用性。
範例
例如下面有兩個component Hello
及Hi
,兩者共有的特色是點擊時下方的Like數會增加。
App.js
import React from "react";
import "./App.css";
function App() {
return (
<div>
<Hello name='John' />
<Hi name='Mary' />
</div>
);
}
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
this.handleLike = this.handleLike.bind(this);
}
handleLike = () => {
let count = this.state.count;
this.setState({count: ++count});
};
render() {
return (
<div>
<h1 onClick={this.handleLike}>Hello, {this.props.name}!</h1>
<span>Like:{this.state.count}</span>
</div>
);
}
}
class Hi extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
this.handleLike = this.handleLike.bind(this);
}
handleLike = () => {
let count = this.state.count;
this.setState({count: ++count});
};
render() {
return (
<div>
<h1 onClick={this.handleLike}>Hi, {this.props.name}!</h1>
<span>Like:{this.state.count}</span>
</div>
);
}
}
export default App;
Like依靠component的state及函式實現,要把兩者抽出為可共享的component就可利用render props的技巧。將上面Like的累加行為及狀態被抽出為Like
component,Hello
及Hi
則成為Like
的render props箭頭函式回傳的可渲染物件。原本Hello
及Hi
在被點擊時增加Like數的行為及記錄Like數的狀態都轉由Like
提供。
Like.render()
中this.props.render()
即為App()
中的兩個<Like/>
的render
props所指的箭頭函式。函式接收兩個參數,第一個參數為function handleLike
,第二個參數為count
數,並轉給函式返回的<Hello/>
及<Hi/>
作為onClick
的事件處理器及顯示Like數的值。
App.js
import React from "react";
import "./App.css";
function App() {
return (
<div>
<Like
render={(handleLike, count) => (
<Hello like={handleLike} count={count} name='John' />
)}
/>
<Like
render={(handleLike, count) => (
<Hi like={handleLike} count={count} name='Mary' />
)}
/>
</div>
);
}
class Like extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
this.handleLike = this.handleLike.bind(this);
}
handleLike = () => {
let count = this.state.count;
this.setState({count: ++count});
};
render() {
return <div>{this.props.render(this.handleLike, this.state.count)}</div>;
}
}
class Hello extends React.Component {
render() {
return (
<div>
<h1 onClick={this.props.like}>Hello, {this.props.name}</h1>
<span>Like:{this.props.count}</span>
</div>
);
}
}
class Hi extends React.Component {
render() {
return (
<div>
<h1 onClick={this.props.like}>Hi, {this.props.name}</h1>
<span>Like:{this.props.count}</span>
</div>
);
}
}
export default App;
Render Props搭配HOC
以Higher-Order Component(HOC) withLike()
改寫如下,可看作是對<Hello/>
及<Hi/>
加上<Like/>
的增強。
App.js
import React from "react";
import "./App.css";
function App() {
const HelloWithLike = withLike(Hello, {name: "John"});
const HiWithLike = withLike(Hi, {name: "Mary"});
return (
<div>
<HelloWithLike />
<HiWithLike />
</div>
);
}
// HOC
function withLike(Component, props) {
return class extends React.Component {
render() {
return (
<Like
render={(handleLike, count) => (
<Component like={handleLike} count={count} {...props} />
)}
/>
);
}
};
}
class Like extends React.Component {...}
class Hello extends React.Component {...}
class Hi extends React.Component {...}
export default App;
Render Props搭配props.children
上面提到render props可以為任意名稱的props,把Like.render()
中的this.props.render
改為this.props.children
,利用props.children
這特殊的props,如此render props的函式的就不用寫在<Component/>
的屬性中,可以直接放在<Component>{...}</Component>
間。
App.js
import React from "react";
import "./App.css";
function App() {...}
function withLike(Component, props) {
return class extends React.Component {
render() {
return ( // move props.children from attribute to element content
<Like>
{(handleLike, count) => (
<Component {...props} like={handleLike} count={count} />
)}
</Like>
);
}
};
}
class Like extends React.Component {
...
render() { // use props.children as render props
return <div>{this.props.children(this.handleLike, this.state.count)}</div>;
}
}
class Hello extends React.Component {...}
class Hi extends React.Component {...}
export default App;
參考github。
Function Component改寫
把上面範例用Function Component改寫如下。
App.js
import React, {useState} from "react";
import "./App.css";
function App() {
const HelloWithLike = withLike(Hello, {name: "John"});
const HiWithLike = withLike(Hi, {name: "Mary"});
return (
<div>
<HelloWithLike />
<HiWithLike />
</div>
);
}
const withLike = (Component, props) => {
return () => (
<Like>
{(handleLike, count) => (
<Component {...props} like={handleLike} count={count} />
)}
</Like>
);
};
const Like = (props) => {
const [count, setCount] = useState(0);
const handleLike = () => {
setCount(count + 1);
};
return <div>{props.children(handleLike, count)}</div>;
};
const Hello = (props) => {
return (
<div>
<h1 onClick={props.like}>Hello, {props.name}</h1>
<span>Like:{props.count}</span>
</div>
);
};
const Hi = (props) => {
return (
<div>
<h1 onClick={props.like}>Hi, {props.name}</h1>
<span>Like:{props.count}</span>
</div>
);
};
export default App;
沒有留言:
張貼留言