写React差不多快两年了,几乎每天都可以写出这样的代码
export default class App extends Component {
state = { loading: true };
componentDidMount() {
fetch("https://swapi.co/api/planets/5")
.then(res => res.json())
.then(
planet => this.setState({ loading: false, planet }),
error => this.setState({ loading: false, error })
);
}
renderLoading() {
return <div>Loading...</div>;
}
renderError() {
return <div>I'm sorry! Please try again.</div>;
}
renderPlanet() {
const { name, climate, terrain } = this.state.planet;
return (
<div>
<h2>{name}</h2>
<div>Climate: {climate}</div>
<div>Terrain: {terrain}</div>
</div>
);
}
render() {
if (this.state.loading) {
return this.renderLoading();
} else if (this.state.planet) {
return this.renderPlanet();
} else {
return this.renderError();
}
}
}
当我们通过fetch请求数据时,我们需要定义一些state来标记请求的状态和结果。
但是这样做会存在什么问题呢?
如果我们想要使用storybook来呈现三种状态下的界面概览,可以做到吗? 如果要在不请求数据的情况做单元测试,
一种简单的好的模式,把逻辑和view分开处理
The Container / View Pattern
Class PlanetView extends Component {
renderLoading() {
return <div>Loading...</div>;
}
renderError() {
return <div>I'm sorry! Please try again.</div>;
}
renderPlanet() {
const { name, climate, terrain } = this.props.planet;
return (
<div>
<h2>{name}</h2>
<div>Climate: {climate}</div>
<div>Terrain: {terrain}</div>
</div>
);
}
render() {
if (this.props.loading) {
return this.renderLoading();
} else if (this.props.planet) {
return this.renderPlanet();
} else {
return this.renderError();
}
}
}
class DagobahContainer extends React.Component {
state = { loading: true };
componentDidMount() {
fetch("https://swapi.co/api/planets/5")
.then(res => res.json())
.then(
planet => this.setState({ loading: false, planet }),
error => this.setState({ loading: false, error })
);
}
render() {
return <PlanetView {...this.state} />;
}
}
Higher Order Components
const withDagobah = PlantViewComponent =>
class extends Component {
state = { loading: true };
componentDidMount() {
fetch("https://swapi.co/api/planets/5")
.then(res => res.json())
.then(
planet => this.setState({ loading: false, planet }),
error => this.setState({ loading: false, error })
);
}
render() {
return <PlanetViewComponent {...this.state} />;
}
}
};
export default withDagobah(PlanetBranch);
Render Props common
const App = React.createClass({
getInitialState() {
return { x: 0, y: 0 }
},
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
})
},
render() {
const { x, y } = this.state
return (
<div style= onMouseMove={this.handleMouseMove}>
<h1>The mouse position is ({x}, {y})</h1>
</div>
)
}
})s
HOC
const withMouse = (Component) => {
return class extends React.Component {
state = { x: 0, y: 0 }
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style= onMouseMove={this.handleMouseMove}>
<Component {...this.props} mouse={this.state}/>
</div>
)
}
}
}
const App = React.createClass({
render() {
// Instead of maintaining our own state,
// we get the mouse position as a prop!
const { x, y } = this.props.mouse
return (
<div style=>
<h1>The mouse position is ({x}, {y})</h1>
</div>
)
}
})
// Just wrap your component in withMouse and
// it'll get the mouse prop!
const AppWithMouse = withMouse(App)
不足:我们不知道哪个HOC提供了Props; props命名冲突
###Render Props
class Mouse extends React.Component {
static propTypes = {
render: PropTypes.func.isRequired
}
state = { x: 0, y: 0 }
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style= onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
)
}
}
const App = React.createClass({
render() {
return (
<div style=>
<Mouse render={({ x, y }) => (
// The render prop gives us the state we need
// to render whatever we want here.
<h1>The mouse position is ({x}, {y})</h1>
)}/>
</div>
)
}
})