React技术栈之React(二)组件的prop和state
React技术栈之React(一)初识React 组件是React的基石,所有的React应用程序都是基于组件的。基于组件的应用开发是广泛使用的软件开发模式,用分而治之的方法,把一个大的应用分解成若干小的组件,每个组件只关注某个特定功能,但是把组件组合起来,就能构成一个功能庞大的应用。 React组件的数据分为两种,prop和state,无论prop或者state的改变,都可能引发组件的重新渲染。prop是组件对外接口,state是组件内部状态。 第一个组件用create-react-app工具,初始化一个React项目: npm create-react-app react-component-demo 创建一个可以计算点击数的组件: import React from 'react'; class ClickCounter extends React.Component { constructor(props) { super(props); this.onClickButton = this.onClickButton.bind(this); this.state = {count: 0}; } onClickButton() { this.setState({count: this.state.count + 1}); } render() { return ( <div> <button onClick={this.onClickButton}>Click Me</button> <div> Click Count: {this.state.count} </div> </div> ); }; } export default ClickCounter; 修改/src/index.js: import React from 'react'; import ReactDOM from 'react-dom'; import ClickCounter from './ClickCounter'; ReactDOM.render(<ClickCounter />,document.getElementById('root')); 运行React项目 npm run start
点击按钮,数字会随之增加。恭喜你,已经构建了一个有交互的组件! 我们还可以在React组件中定义样式。修改ClickCounter组件的render函数: render() { const counterStyle = { margin: '16px' }; return ( <div style={counterStyle}> <button onClick={this.onClickButton}>Click Me</button> <div> Click Count: <span id="clickCount">{this.state.count}</span> </div> </div> ); }; 组件的propReact组件通过定义自己能够接受的prop就定义了自己对外公共接口。外部世界通过prop和组件对话。 给prop赋值从外部世界看prop的使用: <SampleButton id="sample" borderWidth={2} onClick={onButtonClick} style={{color: "red"}} /> 上面的例子使用了名为SampleButton的组件实例。React组件的prop所能支持的类型除了字符串,还可以是任何一种JavaScript语言支持的数据类型。当prop的类型不是字符串时,再JSX中必须用花括号{}把值包裹,所以style的值有两层花括号,外层代表是JSX的语法,内层代表这是个对象常量。 React组件要反馈数据给外部世界,也是用prop,因为prop类型也可以是函数,函数类型的prop等于让父组件交给子组件一个回调函数,子组件在恰当的时机调用函数的prop,就可以把信息传递给外部世界。 为了演示,我们构造一个应用包含两种组件,ControlPanel父组件,然后若干个Counter子组件。对于Counter组件,父组件ControlPanel就是外部世界: class ControlPanel extends React.Component { render() { return ( <div> <Counter caption="First" initValue={0} /> <Counter caption="Second" initValue={10} /> <Counter caption="Third" initValue={20} /> </div> ); } }
每个Counter组件使用了caption和initValue两个prop。ControlPanel通过caption的prop传递给Counter组件实例说明文字,通过initValue的prop传递给Count组件一个初始的计数值。 读取prop值看下Counter组件内部是如何接收prop的: class Counter extends React.Component { constructor(props) { super(props); this.onClickIncrementButton = this.onClickIncrementButton.bind(this); this.onClickDecrementButton = this.onClickDecrementButton.bind(this); this.state = { count: props.initValue || 0 }; } } 如果组件需要定义自己的构造函数,构造函数第一行一定要通过super调用父类React.Component的构造函数。给this.props赋值也是React.Component构造函数的工作之一。 在其他函数中则可以通过this.props访问传入的值,看一下render函数: render() { const {caption} = this.props; // ES6的解构赋值 return ( <div> <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button> <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button> <span>{caption} count: {this.state.count}</span> </div> ); }; propTypes检查在ES6方法定义的组件中,可以通过增加类的propTypes属性来定义prop规格。在运行和静态代码检查时,都可以根据propTypes判断外部世界是否正确地使用了组件的属性。 增加Counter组件的propTypes定义: Counter.propTypes = { caption: PropTypes.string.isRequired,initValue: PropTypes.number }; 开发过程中,定义propTypes代码可以避免犯错,但是在发布产品时,可以用babel-react-optimize工具自动去除propTypes,这样部署到产品环境的代码就会更优。 组件的state驱动组件渲染的除了prop,还有state,state代表组件内部状态。由于React组件禁止修改传入的prop,所以需要记录自身数据变化,就要使用state。 初始化state通常在构造函数的结尾处初始化state,就如上面的Counter: constructor(props) { ... this.state = { count: props.initValue || 0 }; } 由于在PropType声明中没有用isRequired,我们需要在代码中判断给定的prop值是否存在,不存在则给一个默认值。我们可以利用React的defaultProps功能,避免判断逻辑这种充斥在构造函数之中,让代码更优。 给Counter组件添加defaultProps代码: Counter.defaultProps = { initValue: 0 }; 构造函数就可以简化了: constructor(props) { ... this.state = { count: props.initValue }; } 读取和更新state通过给button的onClick属性挂载点击事件处理函数,我们可以改变组件的state,以点击“+”按钮的响应函数为例: onClickIncrementButton() { this.setState({count: this.state.count + 1}); } 通过this.state可以读取到组件的当前state。注意的是,改变state必须使用this.setState函数,而不能直接修改this.state。如果你违反这个操作,浏览器Console会告警。 直接修改this.state的值,只是野蛮的修改了state,却没有驱动组件重新渲染,新的值当然也不会反应在界面上。而this.setState()函数所做的事情,就是改变this.state的值后再驱动组件重新渲染。 无状态函数式组件没有内部state,不需要组件生命周期函数。可以用纯函数的形式来表达。它做的事情只是根据输入来展示组件,没有其他副作用。可以把这种组件称为无状态函数式组件(stateless functional component)。 import React from 'react'; // 用一个纯函数表示 const Hobby = (props) => <li>{props.hobby}</li>; export default Hobby; state设计原则创建尽量多的无状态组件,这些组件唯一关心的就是渲染数据。而在最外层,应该有一个包含state的父级别组件,用于处理各种事件、交流逻辑、修改state。对应的子组件要关心的只是传入的属性而已。 state应该包含组件的事件回调函数可能引发UI更新的这类数据。在实际的项目中,应该是轻量化的JSON数据,尽量把数据的表现设计到最小,更多的数据可以在render中通过各种计算得到。 prop和state对比
DOM操作大多数情况下,不需要操作DOM去更新UI,应使用setState。但是有些情况确实需要访问一些DOM(如表单的值),那么可采用refs方式来获得DOM节点。只需要加个ref属性,然后通过this.refs.name来获得对应的DOM结构。 示例Profile组件: render() { return ( <div> ... <input type="text" ref="hobby" /> <button onClick={this.addHobbyCallback}>添加爱好</button> </div> ) } 在button上添加事件,取得input的值,添加到state的值里面: addHobbyCallback() { // 用this.refs.name来取得DOM节点 let hobbyInput = this.refs.hobby; let val = hobbyInput.value; if (val) { let hobbies = this.state.hobbies; // 添加值到数组 hobbies = [...hobbies,val]; // 更新state,刷新UI this.setState({ hobbies },() => { hobbyInput.value = ''; }); } } React技术栈之React(一)初识React (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |