组合模式
react中最大的好处就是组合,把一个大的组件通过划分,用一个个小组件进行一个组合。在本节中我们将探索一些使用组合更好的方式。我们来举个简单的例子,我们假设要创建一个带标题的应用,并且我们希望应用带有导航栏。我们的应用有三个部分App, Header, Navigation.我们的app看起来应该是下面这个样子
<App> -> <Header> -> <Navigation>
每个组件的相互应用关系应该是下面这种依赖关系。
// app.jsx
import Header from './Header.jsx';
export default function App() {
return <Header />;
}
// Header.jsx
import Navigation from './Navigation.jsx';
export default function Header() {
return <header><Navigation /></header>;
}
// Navigation.jsx
export default function Navigation() {
return (<nav> ... </nav>);
}
但是使用这种方式,我们会发现一些问题。
- 看起来我们是把App当做了整个容器,Header可能还有一些组件比如logo,login,search等。可以引入到App中来使用。但是我们发现Header里也耦合了Navagation控件,我们知道两者并不是有所关联。
- 这更加难以测试,比如我们想测试Header组件我们必须要初始化一个实例,但是Header里的Navigation组件影响到了我们测试。
使用react的children属性
export default function App() {
return (
<Header>
<Navigation />
</Header>
);
}
export default function Header({ children }) {
return <header>{ children }</header>;
};
如果这里我们不使用children属性,我们的Navigation组件将不会被渲染,现在我们的组件变得容易测试。
使用props传递组件
const Title = function () {
return <h1>Hello there!</h1>;
}
const Header = function ({ title, children }) {
return (
<header>
{ title }
{ children }
</header>
);
}
function App() {
return (
<Header title={ <Title /> }>
<Navigation />
</Header>
);
};
当一个组件对它的子项进行决策时,这中技术很有用,最简单的例子就是通过条件来判断子组件的可见性。
使用方法渲染一个子组件
在我的例子中我们都是使用一个元素作为一个子组件进行传递,当然我们也可以使用对象作为子组件进行传递。
function UserName({ children }) {
return (
<div>
<b>{ children.lastName }</b>,
{ children.firstName }
</div>
);
}
function App() {
const user = {
firstName: 'Krasimir',
lastName: 'Tsonev'
};
return (
<UserName>{ user }</UserName>
);
}
这看起来有点怪异,但是事实上他是很棒的.当我们不想把父组件的一些东西传给子组件时我们就可以使用这种方式。比如下面这个todolist例子。
function TodoList({ todos, children }) {
return (
<section className='main-section'>
<ul className='todo-list'>{
todos.map((todo, i) => (
<li key={ i }>{ children(todo) }</li>
))
}</ul>
</section>
);
}
function App() {
const todos = [
{ label: 'Write tests', status: 'done' },
{ label: 'Sent report', status: 'progress' },
{ label: 'Answer emails', status: 'done' }
];
const isCompleted = todo => todo.status === 'done';
return (
<TodoList todos={ todos }>
{
todo => isCompleted(todo) ?
<b>{ todo.label }</b> :
todo.label
}
</TodoList>
);
}
当然我们可以使用props去传递这个方法。这其实是个很不错的方案,我们通过这个例子可以看出,我们可以自定义每个cell的样式以及显示。这简直太棒了。我们的TodoList完全是基于data渲染,每个cell上的显示也是基于data渲染的。
function TodoList({ todos, render }) {
return (
<section className='main-section'>
<ul className='todo-list'>{
todos.map((todo, i) => (
<li key={ i }>{ render(todo) }</li>
))
}</ul>
</section>
);
}
return (
<TodoList
todos={ todos }
render={
todo => isCompleted(todo) ?
<b>{ todo.label }</b> : todo.label
} />
);
个人思考:我们在设计组件的时候应该考虑这种设计就是数据渲染,尽量的解耦合。
网友评论