这篇文章是30天React系列的一部分 。
在本系列中,我们将从非常基础开始,逐步了解您需要了解的所有内容,以便开始使用React。如果您曾经想学习React,那么这里就是您的最佳选择!
数据驱动
我们的应用程序中的硬编码数据并不完全理想。今天,我们将我们的组件设置为由数据驱动,以便访问外部数据。
通过这一点,我们编写了第一个组件并将它们设置为子/父关系。但是,我们还没有将任何数据绑定到我们的React组件。虽然在React中编写网站是一种更愉快的体验(我们认为),但我们还没有利用React的强大功能来显示任何动态数据。
让我们今天改变。
走向数据驱动
回想一下,昨天我们构建了包含标题和活动列表的时间轴组件的开头:
我们将演示分解为组件,最后使用静态JSX模板构建了三个独立的组件。每当我们对网站数据进行更改时,必须更新我们组件的模板并不是很方便。
相反,让我们给出用于显示的组件数据。让我们从<Header />
组件开始。就目前而言,该<Header />
组件仅显示元素的标题Timeline
。这是一个很好的元素,能够在页面的其他部分重用它会很好,但标题Timeline
对于每次使用都没有意义。
让我们告诉React我们希望能够将标题设置为其他内容。
介绍道具
React允许我们使用与组件相同的属性或属性,以与HTML相同的语法将数据发送到组件。这类似于将src
属性传递给图像标记。我们可以考虑<img />
标签的属性,因为prop
我们正在设置一个名为的组件img
。
我们可以在组件中访问这些属性this.props
。让我们看看props
行动吧。
回想一下,我们将<Header />
组件定义为:
class Header extends React.Component {
render() {
return (
<div className="header">
<div className="fa fa-more"></div>
<span className="title">Timeline</span>
<input
type="text"
className="searchInput"
placeholder="Search ..." />
<div className="fa fa-search searchIcon"></div>
</div>
)
}
}
当我们使用<Header />
组件时,我们将它放在我们的<App />
组件中,如下所示:
<Header />
我们可以通过更新组件的使用来传递我们title
作为prop的属性,<Header />
将属性调用设置title
为某个字符串,如下所示:
<Header title="Timeline" />
时间在我们的组件内部,我们可以title
从类中的this.props
属性访问此prop Header
。Timeline
我们可以用传入的属性替换它,而不是像在模板中那样静态地设置标题。
class Header extends React.Component {
render() {
return (
<div className="header">
<div className="menuIcon">
<div className="dashTop"></div>
<div className="dashBottom"></div>
<div className="circle"></div>
</div>
<span className="title">
{this.props.title}
</span>
<input
type="text"
className="searchInput"
placeholder="Search ..." />
<div className="fa fa-search searchIcon"></div>
</div>
)
}
}
我们还稍微更新了代码,以便更接近我们最终
<Header />
代码的样子,包括添加searchIcon
一些元素来设置样式menuIcon
。
现在我们的<Header />
组件将显示我们在title
调用组件时传入的字符串。例如,<Header />
像我们这样调用我们的组件四次:
<Header title="Timeline" />
<Header title="Profile" />
<Header title="Settings" />
<Header title="Chat" />
四个<Header />
组件的结果如下所示:
时间线
轮廓
设置
很漂亮,是吗?现在我们可以使用<Header />
动态title
属性重用该组件。
我们可以传递的不仅仅是组件中的字符串。我们可以传递数字,字符串,各种对象,甚至函数!我们将更多地讨论如何定义这些不同的属性,以便我们稍后可以构建组件api。
而不是静态设置内容和日期让我们采取Content
组件并通过数据变量而不是文本设置时间轴内容。就像我们可以使用HTML组件一样,我们可以将多个props
组件传递给组件。
回想一下,昨天我们定义了这样的Content
容器:
class Content extends React.Component {
render() {
return (
<div className="content">
<div className="line"></div>
{/* Timeline item */}
<div className="item">
<div className="avatar">
<img src="http://www.croop.cl/UI/twitter/images/doug.jpg" />
Doug
</div>
<span className="time">
An hour ago
</span>
<p>Ate lunch</p>
<div className="commentCount">
2
</div>
</div>
{/* ... */}
</div>
)
}
}
正如我们所做的那样title
,让我们来看看props
我们的Content
组件需要什么:
- 用户的头像图像
- 活动的时间戳
- 活动项目的文本
- 评论数量
假设我们有一个表示活动项的JavaScript对象。我们将有一些字段,例如字符串字段(文本)和日期对象。我们可能有一些嵌套对象,比如a user
和comments
。例如:
const moment1 = {
timestamp: new Date().getTime(),
text: "Ate lunch",
user: {
id: 1,
name: 'Nate',
avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [
{ from: 'Ari', text: 'Me too!' }
]
}
就像我们将字符串标题传递给<Header />
组件一样,我们可以获取此活动对象并将其直接传递给Content
组件。让我们转换我们的组件,在其模板中显示此活动的详细信息。
为了将动态变量的值传递给模板,我们必须使用模板语法在模板中呈现它。例如:
class Content extends React.Component {
render() {
const {activity} = this.props; // ES6 destructuring
return (
<div className="content">
<div className="line"></div>
{/* Timeline item */}
<div className="item">
<div className="avatar">
<img
alt={activity.text}
src={activity.user.avatar} />
{activity.user.name}
</div>
<span className="time">
{activity.timestamp}
</span>
<p>{activity.text}</p>
<div className="commentCount">
{activity.comments.length}
</div>
</div>
</div>
)
}
}
我们在
render()
名为destructuring的函数的第一行中的类定义中使用了一些ES6 。以下两行在功能上是等效的:// these lines do the same thing const activity = this.props.activity; const {activity} = this.props;
解构允许我们以更短,更紧凑的方式节省打字和定义变量。
然后,我们可以通过将对象作为prop而不是硬编码字符串传递来使用此新内容。例如:
<Content activity={moment1} />
太棒了,现在我们的活动项目由一个对象驱动。但是,您可能已经注意到我们必须使用不同的注释多次实现此操作。相反,我们可以将一组对象传递给一个组件。
假设我们有一个包含多个活动项的对象:
const activities = [
{
timestamp: new Date().getTime(),
text: "Ate lunch",
user: {
id: 1, name: 'Nate',
avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [{ from: 'Ari', text: 'Me too!' }]
},
{
timestamp: new Date().getTime(),
text: "Woke up early for a beautiful run",
user: {
id: 2, name: 'Ari',
avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [{ from: 'Nate', text: 'I am so jealous' }]
},
]
我们可以<Content />
通过传递多个活动而不是仅仅一个来重新表达我们的用法:
<Content activities={activities} />
但是,如果我们刷新视图,则不会显示任何内容!我们需要先更新我们的Content
组件以接受多个活动。正如我们之前所了解的,JSX实际上只是由浏览器执行的JavaScript。我们可以在JSX内容中执行JavaScript函数,因为它将像我们的其他JavaScript一样由浏览器运行。
让我们将活动项目JSX移动map
到我们将为每个项目运行的函数的函数内部。
class Content extends React.Component {
render() {
const {activities} = this.props; // ES6 destructuring
return (
<div className="content">
<div className="line"></div>
{/* Timeline item */}
{activities.map((activity) => {
return (
<div className="item">
<div className="avatar">
<img
alt={activity.text}
src={activity.user.avatar} />
{activity.user.name}
</div>
<span className="time">
{activity.timestamp}
</span>
<p>{activity.text}</p>
<div className="commentCount">
{activity.comments.length}
</div>
</div>
);
})}
</div>
)
}
}
现在我们可以将任意数量的活动传递给我们的数组,Content
组件将处理它,但是如果我们现在离开组件,那么我们将有一个相对复杂的组件处理包含和显示活动列表。像这样离开它真的不是React方式。
ActivityItem
在这里,编写一个组件以包含显示单个活动项目的意义,然后Content
我们可以转移责任,而不是构建一个复杂的组件。这也将使测试,添加功能等更容易。
让我们更新我们的Content
组件以显示组件列表ActivityItem
(我们将在下一步创建)。
class Content extends React.Component {
render() {
const {activities} = this.props; // ES6 destructuring
return (
<div className="content">
<div className="line"></div>
{/* Timeline item */}
{activities.map((activity) => (
<ActivityItem
activity={activity} />
))}
</div>
)
}
}
这不仅更简单,更容易理解,而且可以更轻松地测试这两个组件。
使用我们新鲜的Content
组件,让我们创建ActivityItem
组件。由于我们已经为此创建了视图,因此ActivityItem
我们需要做的就是从Content
组件的模板中复制它作为自己的模块。
class ActivityItem extends React.Component {
render() {
const {activity} = this.props; // ES6 destructuring
return (
<div className="item">
<div className="avatar">
<img
alt={activity.text}
src={activity.user.avatar} />
{activity.user.name}
</div>
<span className="time">
{activity.timestamp}
</span>
<p>{activity.text}</p>
<div className="commentCount">
{activity.comments.length}
</div>
</div>
)
}
}
最后是我的实践代码:
文档目录
创建文件
- Timeline.css
- NewTimeline.html
- UpdateTimeline.html
最后在浏览器打开以上的html代码即可运行
作者本人的代码封装的很好,值得学习
PS: 当然,你也要去配置react环境才行 :)
Timeline.css
.notificationsFrame {
z-index: 2;
width: 100%;
top: 20px;
background: #fff;
border-radius: 3px;
overflow: hidden;
font-family: 'Open Sans', Helvetica, sans-serif;
margin-bottom: 40px;
}
.notificationsFrame.show-menu {
transform: translate3d(150px, 0, 0);
}
.notificationsFrame .searchInput {
border: 10px solid red;
box-sizing: border-box;
position: absolute;
top: 13px;
right: 55px;
width: 200px;
height: 34px;
border-radius: 17px;
border: none;
background: #fff;
padding: 0 17px;
font-size: 13px;
color: #666;
transition: all 0.3s ease-in-out;
transform: translateX(15px);
opacity: 0;
pointer-events: none;
}
.notificationsFrame .searchInput:focus {
outline: none;
}
.notificationsFrame .searchInput.active {
-webkit-transform: translateX(0);
transform: translateX(0);
opacity: 1;
pointer-events: all;
}
.notificationsFrame .header {
position: relative;
height: 60px;
background: #5f98cd;
padding-top: 0;
}
.notificationsFrame .header .menuIcon {
position: absolute;
width: 29px;
height: 15px;
top: 23px;
left: 20px;
cursor: pointer;
}
.notificationsFrame .header .menuIcon:hover .dashTop,
.notificationsFrame .header .menuIcon:hover .dashBottom,
.notificationsFrame .header .menuIcon:hover .circle {
background: #fff;
}
.notificationsFrame .header .menuIcon .dashTop {
position: absolute;
width: 20px;
height: 3px;
top: 0;
left: 0;
background: #b2daff;
border-radius: 3px;
-webkit-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.notificationsFrame .header .menuIcon .dashBottom {
position: absolute;
width: 20px;
height: 3px;
top: 0;
left: 0;
background: #b2daff;
border-radius: 3px;
-webkit-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
width: 29px;
top: auto;
bottom: 0;
}
.notificationsFrame .header .menuIcon .circle {
position: absolute;
height: 7px;
width: 7px;
border-radius: 4px;
top: -2px;
right: 0;
background: #b2daff;
-webkit-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.notificationsFrame .header .title {
display: block;
text-align: center;
color: #fff;
font-weight: 600;
font-size: 15px;
padding-top: 20px;
}
.notificationsFrame .header .searchIcon {
position: absolute;
z-index: 3;
font-size: 21px;
color: #fff;
top: 18px;
right: 20px;
-webkit-transition: all 0.3s ease;
transition: all 0.3s ease;
cursor: pointer;
}
.notificationsFrame .header .searchIcon:hover {
color: #fff;
}
.notificationsFrame .content {
position: relative;
height: 100%;
overflow: hidden;
}
.notificationsFrame .content .line {
position: absolute;
top: 0;
left: 40px;
bottom: 0;
width: 3px;
background: #ebebeb;
}
.notificationsFrame .content .item {
position: relative;
z-index: 2;
margin: 20px 30px 30px 70px;
display: block;
/*border-radius: 50%;
border: 5px solid #ecf0f1;
box-sizing: border-box;
position: absolute;
height: 20px;
width: 20px;
background: #fff;
border: 2px solid #5F98CD;
box-shadow: 0 0 0 3px #fff;*/
}
.notificationsFrame .content .item:hover {
color: #5f98cd;
cursor: pointer;
}
.notificationsFrame .content .item .circle {
box-sizing: border-box;
position: absolute;
height: 11px;
width: 11px;
background: #fff;
border: 2px solid #5f98cd;
box-shadow: 0 0 0 3px #fff;
border-radius: 6px;
top: 0;
left: -20px;
}
.notificationsFrame .content .item .avatar {
position: absolute;
height: 40px;
width: 40px;
display: inline-block;
vertical-align: top;
overflow: hidden;
left: -49px;
}
.notificationsFrame .content .item .avatar img {
width: 100%;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
-ms-border-radius: 50%;
-o-border-radius: 50%;
border-radius: 50%;
position: absolute;
left: 0;
top: 0;
}
.notificationsFrame .content .item .time {
display: block;
font-size: 11px;
line-height: 11px;
margin-bottom: 2px;
}
.notificationsFrame .content .item p {
font-size: 15px;
line-height: 20px;
margin: 0px 40px 0px 0px;
font-family: 'Open Sans', Lora, Times, no-serif;
}
.notificationsFrame .content .item p b {
font-weight: 600;
}
.notificationsFrame .content .item .right {
position: absolute;
right: 5px;
font-size: 11px;
top: 11px;
}
.notificationsFrame .content .item .commentCount {
position: absolute;
right: 15px;
font-size: 12px;
top: 11px;
}
.notificationsFrame .content .item .commentCount:after {
content: "\f075";
font-family: FontAwesome;
position: absolute;
font-size: 20px;
color: #ebebeb;
top: -50%;
left: 100%;
margin-left: 10px;
z-index: 3;
}
.notificationsFrame .content .item .commentCount:hover:after {
color: lightblue;
}
.notificationsFrame .footer {
position: relative;
background: #fff;
margin: auto;
height: 30px;
border-top: 1px solid #eee;
width: 100%;
border-radius: 10px;
}
.notificationsFrame .footer button {
background: #eee;
position: absolute;
width: 100%;
right: 0px;
left: 0px;
top: 0px;
bottom: 0px;
border: 0;
}
.notificationsFrame .footer button i {
margin: 0 10px;
}
2.NewTimeline.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello world</title>
<link href='Timeline.css' rel="stylesheet" type="text/css" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- Script tags including React -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Header extends React.Component {
render() {
return (
<div className="header">
<div className="menuIcon">
<div className="dashTop"></div>
<div className="dashBottom"></div>
<div className="circle"></div>
</div>
<span className="title">
{/* Timeline */}
{this.props.title}
</span>
<input
type="text"
className="searchInput"
placeholder="Search ..." />
<div className="fa fa-search searchIcon"></div>
</div>
)
}
}
const moment1 = {
timestamp:new Date().getTime(),
text:"Ate lunch",
user:{
id: 1,
name: 'Nate',
avatar:"http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [
{from:'Ari', text:'Me too!'}
]
}
class Content extends React.Component{
render(){
// these lines do the same thing
// const activity = this.props.activity;
//const {activity}= this.props;
//{}对象作为 activities
const {activities}= this.props; // ES6 destructuring
return (
<div className="content">
<div className="line"></div>
{/* Timeline item */}
{/* 使用map是将数组转化为map
*使用地图重新格式化阵列中的对象(activity) 叫obj也可以
*对每一个对象进行操作
*/}
{activities.map((activity) =>{
return(
<div className="item">
<div className = "avatar">
<img
alt={activity.text}
src={activity.user.avatar} />
{activity.user.name}
</div>
<span className = "time">
{activity.timestamp}
</span>
<p>{activity.text}</p>
<div className="commentCount">
{activity.comments.length}
</div>
</div>
);
})}
{/* ... */}
</div>
)
}
}
//使用一个包含多个活动项的对象
const activities = [
{
timestamp:new Date().getTime(),
text:"Ate lunch",
user:{
id: 1,name: 'Nate',
avatar:"http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [{from:'Ari', text:'Me too!'}]
},
{
timestamp:new Date().getTime(),
text:"Woke up early for a beautiful run",
user:{
id: 2,name: 'Ari',
avatar:"http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [{ from: 'Nate', text: 'I am so jealous'}]
},
]
class App extends React.Component {
render() {
return (
<div className="notificationsFrame">
<div className="panel">
{/* content goes here */}
<Header title="Timeline1"/>
<Header title="Profile"/>
<Header title="Settings"/>
<Header title="Timeline1"/>
<Content activities = {activities}/>
</div>
</div>
)
}
}
var mount = document.querySelector('#app');
ReactDOM.render(<App />, mount);
</script>
</body>
</html>
3.UpdateTimeline.html
<!-- 让我们更新我们的Content组件以显示组件列表ActivityItem-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello world</title>
<link href='Timeline.css' rel="stylesheet" type="text/css" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- Script tags including React -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
class Header extends React.Component {
render() {
return (
<div className="header">
<div className="menuIcon">
<div className="dashTop"></div>
<div className="dashBottom"></div>
<div className="circle"></div>
</div>
<span className="title">
{/* Timeline */}
{this.props.title}
</span>
<input
type="text"
className="searchInput"
placeholder="Search ..." />
<div className="fa fa-search searchIcon"></div>
</div>
)
}
}
const moment1 = {
timestamp:new Date().getTime(),
text:"Ate lunch",
user:{
id: 1,
name: 'Nate',
avatar:"http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [
{from:'Ari', text:'Me too!'}
]
}
class Content extends React.Component{
render(){
const {activities}= this.props; // ES6 destructuring
return (
<div className="content">
<div className="line"></div>
{/* Timeline item */}
{activities.map((activity) =>
return(
<ActivityItem
activity = {activity}
/>
))}
{/* ... */}
</div>
)
}
}
class ActivityItem extends React.Component{
const {activity} = this.props;//ES6 destructuring
render(){
return (
<div className="item">
<div className = "avatar">
<img
alt={activity.text}
src={activity.user.avatar} />
{activity.user.name}
</div>
<span className = "time">
{activity.timestamp}
</span>
<p>{activity.text}</p>
<div className="commentCount">
{activity.comments.length}
</div>
</div>
)
}
}
//使用一个包含多个活动项的对象
const activities = [
{
timestamp:new Date().getTime(),
text:"Ate lunch",
user:{
id: 1,name: 'Nate',
avatar:"http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [{from:'Ari', text:'Me too!'}]
},
{
timestamp:new Date().getTime(),
text:"Woke up early for a beautiful run",
user:{
id: 2,name: 'Ari',
avatar:"http://www.croop.cl/UI/twitter/images/doug.jpg"
},
comments: [{ from: 'Nate', text: 'I am so jealous'}]
},
]
class App extends React.Component {
render() {
return (
<div className="notificationsFrame">
<div className="panel">
{/* content goes here */}
<Header title="Timeline1"/>
<Header title="Profile"/>
<Header title="Settings"/>
<Header title="Timeline1"/>
<Content activities = {activities}/>
</div>
</div>
)
}
}
var mount = document.querySelector('#app');
ReactDOM.render(<App />, mount);
</script>
</body>
</html>
本周,我们使用React props
概念更新了由数据驱动的组件。在下一节中,我们将深入研究有状态组件。
学习REACT正确的方法
React和朋友的最新,深入,完整的指南。
下一章:
状态(State)
https://blog.csdn.net/qq_35812380/article/details/83011062
本教程系列的完整源代码可以在GitHub repo上找到,其中包括所有样式和代码示例。
如果您在任何时候感到困难,还有其他问题,请随时通过以下方式与我们联系:
- 在文章末尾评论这篇文章
- 通过react@fullstackreact.com发送电子邮件给我们
- 加入我们的gitter室
- 发送电子邮件至@fullstackreact
学习构建真正的React应用程序
想了解如何在生产应用程序中使用React?点击下面的电子邮件,我们将通过电子邮件向您发送PDF文件,引导您在React中逐步构建应用程序 - 从空文件夹到工作应用程序。
名字
电子邮件地址
发送我的PDF
没有垃圾邮件,很容易取消订阅。
资源
其他产品
- Fullstack React Book
- Fullstack React Native Book
- 零书反应
- ng-book 2:Angular 2的指南
- ng-newsletter:AngularJS新闻
- 量角器大师班
保持联系
- 有问题吗?
- react@fullstack.io
-
I agree with Floyd. It seems like you kind of skip the webpack portion of setting up this react app, which results in a lack of any sort of visual confirmation that my code is correct.