核心概念
JSX的使用
介绍:JSX是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。
特点
- 在 JSX 中嵌入表达式。在标签中使用{}就可以使用变量和方法。
- JSX 也是一个表达式。也就是说,你可以在 if 语句和 for 循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX。
- JSX 特定属性。通过使用引号,来将属性值指定为字符串字面量;也可以使用大括号,来在属性值中插入一个 JavaScript 表达式,比如使用变量或者一个函数等。在属性中嵌入 JavaScript 表达式时,不要在大括号外面加上引号。你应该仅使用引号(对于字符串值)或大括号(对于表达式)中的一个,对于同一属性不能同时使用这两种符号。
- 使用 JSX 指定子元素。 JSX 标签里能够包含很多子元素
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
-
JSX 防止注入攻击。 React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
-
JSX 表示对象。 Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
// 等同于
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
元素渲染
介绍:元素是构成 React 应用的最小砖块。
特点
- 将一个元素渲染为 DOM。 想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()
- 更新已渲染的元素。 根据我们已有的知识,更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()。
- React 只更新它需要更新的部分。 React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
组件 & Props
介绍:组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。
特点
- 函数组件与 class 组件
- 函数组件:该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。
- 类组件:使用ES6的class属性继承React.Component
// 函数组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 等同于
// 类组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
- 渲染组件
介绍:React不仅仅可以渲染DOM中的标签,也可以渲染自定义组件。当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
渲染的过程:
- 我们调用 ReactDOM.render() 函数,并传入 <Welcome name=“Sara” /> 作为参数。
- React 调用 Welcome 组件,并将 {name: ‘Sara’} 作为 props 传入。
- Welcome 组件将<h1>Hello, Sara</h1> 元素作为返回值。
- React DOM 将 DOM 高效地更新为 <h1>Hello, Sara<\h1>。
- 组合组件
介绍:组件可以在其输出中引用其他组件。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
-
提取组件。 将组件拆分为更小的组件
-
Props 的只读性。组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。
特点:所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
纯函数:该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。
function sum(a, b) {
return a + b;
}
非纯函数示例,因为修改了传入的参数amount和account.total的值
function withdraw(account, amount) {
account.total -= amount;
}
State & 生命周期
- state和props的区别
- State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。
-
将函数组件转换成 class 组件
通过以下五步将 Clock 的函数组件转成 class 组件:- 创建一个同名的 ES6 class,并且继承于 React.Component。
- 添加一个空的 render() 方法。
- 将函数体移动到 render() 方法之中。
- 在 render() 方法中使用 this.props 替换 props。
- 删除剩余的空函数声明。
-
向 class 组件中添加局部的 state
我们通过以下三步将 date 从 props 移动到 state 中:-
把 render() 方法中的 this.props.date 替换成 this.state.date
-
添加一个 class 构造函数,然后在该函数中为 this.state 赋初值:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
-
移除 <Clock /> 元素中的 date 属性,因为当前组件已经可以通过state获取到date的值
-
-
将生命周期方法添加到 Class 中
常用的生命周期函数。-
挂载阶段
constructor:当组件创建的时候调用componentWillMount:当组件被挂载到页面的时候执行(挂载页面之前)
componentDidMount:当组件挂载之后执行(页面渲染之后)
render: 当state或者props改变的时候调用
-
更新阶段
shouldComponentUpdate:组件更新之前它会自动执行componentWillUpdate:组件更新之前,它会自动执行,但是在shouldComponentUpdate之后被执行,如果shouldComponentUpdate返回值是true才被执行,如果返回值是false,则不执行页面也不会更新。
componentWillReceiveProps:两种情况会执行,第一种情况当一个组件从父组件接收了参数;第二种情况,只要父组件的render函数被执行了,子组件的这个生命周期函数才会被执行。
render: 当state或者props改变的时候调用
-
卸载阶段
componentWillUnmount:当组件即将卸载之前,页面销毁之前调用。
-
-
正确地使用 State
setState() 你应该了解三件事-
不要直接修改 State
构造函数是唯一可以给 this.state 赋值的地方,其余地方修改state的值应该通过setState的方式修改。 -
State 的更新可能是异步的
因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数
// Correct this.setState((state, props) => ({ counter: state.counter + props.increment }));
-
State 的更新会被合并,当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。
这里的合并是浅合并,所以 this.setState({comments}) 完整保留了 this.state.posts, 但是完全替换了 this.state.comments。
componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); }
-
-
数据是向下流动的
这通常会被叫做“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。组件可以选择把它的 state 作为 props 向下传递到它的子组件中。
事件处理
特点:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
- 在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault 。例如,
/** 传统的 HTML 中阻止链接默认打开一个新页面,你可以这样写 */
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
// React的写法
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
注意点:
必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
解决方式有两种
- 使用 class fields 正确的绑定回调函数
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
- 回调中使用箭头函数
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
这两种方式的区别是:
- 第二种方式问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
- 向事件处理传递参数时,使用bind绑定的方式,传参是隐式传参;但是使用箭头函数是显式传参,即函数需要必传的参数必须传入。
向事件处理程序传递参数
两种实现参数传递的方式
- funtion使用bind绑定this
- 箭头函数
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
两种区别:在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。
条件渲染
介绍:在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。
- 元素变量
说明:使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分,而其他的渲染部分并不会因此而改变。
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
- 与运算符 &&
说明:通过花括号包裹代码,你可以在 JSX 中嵌入任何表达式。这也包括 JavaScript 中的逻辑与 (&&) 运算符。它可以很方便地进行元素的条件渲染。
实现的思路:在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false。因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
- 三目运算符
说明:另一种内联条件渲染的方法是使用 JavaScript 中的三目运算符 condition ? true : false。
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
- 阻止组件渲染
说明:在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。
注意点:在组件的 render 方法中返回 null 并不会影响组件的生命周期。例如,下面这个示例中,componentDidUpdate 依然会被调用。因为组件并未卸载。
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
列表 & Key
- 渲染多个组件
说明:你可以通过使用 {} 在 JSX 内构建一个元素集合
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
- 基础列表组件
说明:通常你需要在一个组件中渲染列表。我们可以把前面的例子重构成一个组件,这个组件接收 numbers 数组作为参数并输出一个元素列表。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
- key
说明:key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
注意点:- 一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key
- 当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key
- 如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
- 元素的 key 只有放在就近的数组上下文中才有意义
- key 只是在兄弟节点之间必须唯一
Robin Pokorny 的深度解析使用索引作为 key 的负面影响
为什么Key值是唯一的
-
用 key 提取组件
说明:如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。一个好的经验法则是:在 map() 方法中的元素需要设置 key 属性。
-
key 只是在兄弟节点之间必须唯一
说明:数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值 -
在 JSX 中嵌入 map()
说明:JSX 允许在大括号中嵌入任何表达式,所以我们可以内联 map() 返回的结果
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
表单
-
受控组件
说明:使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。对于受控组件来说,输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI 元素,或者通过其他事件处理函数重置,但这意味着你需要编写更多的代码。
-
select 标签
与其他的受控组件的不同地方:React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择你喜欢的风味:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
注意点:
你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项:
<select multiple={true} value={['B', 'C']}>
- 文件 input 标签
说明:在 HTML 中,<input type=“file”> 允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API进行控制。
注意点:因为它的 value 只读,所以它是 React 中的一个非受控组件。将与其他非受控组件在后续文档中一起讨论。
<input type="file" />
- 处理多个输入
说明:当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.name === 'isGoing' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
参与:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
来宾人数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
-
受控输入空值
说明:在受控组件上指定 value 的 prop 会阻止用户更改输入。如果你指定了 value,但输入仍可编辑,则可能是你意外地将value 设置为 undefined 或 null。输入最初被锁定,但在短时间延迟后变为可编辑。
ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
- 受控组件的替代品
说明:有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。 - 成熟的解决方案
说明:表单验证等拓展性内容,需要深入的学习。
状态提升
说明:通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
总结:
1. 在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠 自上而下 的数据流,而不是尝试在不同组件间同步 state。
2. 虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。
3. 如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。
组合 vs 继承
-
组合
- 包含关系
说明:有些组件无法提前知晓它们子组件的具体内容。function FancyBorder(props) { return ( <div className={'FancyBorder FancyBorder-' + props.color}> {props.children} </div> ); }
- 特例关系
说明:“特殊”组件可以通过 props 定制并渲染“一般”组件function Dialog(props) { return ( <FancyBorder color="blue"> <h1 className="Dialog-title"> {props.title} </h1> <p className="Dialog-message"> {props.message} </p> </FancyBorder> ); } function WelcomeDialog() { return ( <Dialog title="Welcome" message="Thank you for visiting our spacecraft!" /> ); }
- 包含关系
-
继承
Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。
React 设计思想
React 最棒的部分之一是引导我们思考如何构建一个应用。
- 从设计稿开始
-
第一步:将设计好的 UI 划分为组件层级
首先,你需要在设计稿上用方框圈出每一个组件(包括它们的子组件),并且以合适的名称命名。但你如何确定应该将哪些部分划分到一个组件中呢?你可以将组件当作一种函数或者是对象来考虑,根据单一功能原则来判定组件的范围。也就是说,一个组件原则上只能负责一个功能。如果它需要负责更多的功能,这时候就应该考虑将它拆分成更小的组件。
在实践中,因为你经常是在向用户展示 JSON 数据模型,所以如果你的模型设计得恰当,UI(或者说组件结构)便会与数据模型一一对应,这是因为 UI 和数据模型都会倾向于遵守相同的信息结构。将 UI 分离为组件,其中每个组件需与数据模型的某部分匹配。
-
第二步:用 React 创建一个静态版本
实现方式:最容易的方式,是先用已有的数据模型渲染一个不包含交互功能的 UI。最好将渲染 UI 和添加交互这两个过程分开。这是因为,编写一个应用的静态版本时,往往要编写大量代码,而不需要考虑太多交互细节;添加交互功能时则要考虑大量细节,而不需要编写太多代码。所以,将这两个过程分开进行更为合适。我们会在接下来的代码中体会到其中的区别。注意点:
- 最容易的方式,是先用已有的数据模型渲染一个不包含交互功能的 UI。最好将渲染 UI 和添加交互这两个过程分开。这是因为,编写一个应用的静态版本时,往往要编写大量代码,而不需要考虑太多交互细节;添加交互功能时则要考虑大量细节,而不需要编写太多代码。所以,将这两个过程分开进行更为合适。我们会在接下来的代码中体会到其中的区别。
- 由于我们构建的是静态版本,所以这些组件目前只需提供 render() 方法用于渲染。
你可以自上而下或者自下而上构建应用:自上而下意味着首先编写层级较高的组件(比如 FilterableProductTable),自下而上意味着从最基本的组件开始编写(比如 ProductRow)。当你的应用比较简单时,使用自上而下的方式更方便;对于较为大型的项目来说,自下而上地构建,并同时为低层组件编写测试是更加简单的方式。
-
第三步:确定 UI state 的最小(且完整)表示。
说明:首先需要找出应用所需的 state 的最小表示,并根据需要计算出其他所有数据。通过问自己以下三个问题,你可以逐个检查相应数据是否属于 state:
- 该数据是否是由父组件通过 props 传递而来的?如果是,那它应该不是 state。
- 该数据是否随时间的推移而保持不变?如果是,那它应该也不是 state。
- 你能否根据其他 state 或 props 计算出该数据的值?如果是,那它也不是 state。
-
第四步:确定 state 放置的位置
注意:React 中的数据流是单向的,并顺着组件层级从上往下传递。哪个组件应该拥有state的判断:
- 对于应用中的每一个 state
- 找到根据这个 state 进行渲染的所有组件。
- 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
- 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
- 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。
- 对于应用中的每一个 state
-
第五步:添加反向数据流
实现的方式:
例如:子组件输入数据,需要调用父组件提供的callback方法,将数据作为参数传给父组件的方法进行修改,父组件的state数据发生改变。
-