React技术栈之React(二)组件
React组件组件是React的基石,所有的React应用程序都是基于组件的。 import React from 'react'; var List = React.createClass({ getInitialState: function() { return ['a','b','c'] },render: function() { return (...); } }); export default List; 上面的做法已不推荐,建议全部使用ES6写法,React官方也在第一时间就支持了ES6 class的写法。 ES6语法定义组件: import React from 'react'; class List extends React.Component { constructor() { super(); this.state = ['a','c']; } render() { return (...); } } export default List; 组件首字母必需大写。JSX语法使用第一个字母大小写来区分普通的HTML标签和React组件。 props属性在组件内部通过this.props访问外部JSX传入的属性。 组件Profile: import React from 'react'; export default class Profile extends React.Component { // 渲染一个Vitrual DOM结构 render() { return ( <div className="profile-component"> <h1>我的名字叫{this.props.name}</h1> <h2>我今年{this.props.age}岁</h2> </div> ) } } 渲染Profile组件: import ReactDOM from 'react-dom'; import Profile from './profile'; const app = document.createElement('div'); document.body.appendChild(app); ReactDOM.render(<Profile name="zhutx" age=30 />,app); 或者使用...属性扩展 const props = { name: 'zhutx',age: 30 }; const app = document.createElement('div'); document.body.appendChild(app); render(<Profile {...props} />,app); React支持组件对传入属性的类型校验: import { PropTypes } from 'react'; const propTypes = { // 验证不同类型的JavaScript变量 optionalArray: PropTypes.array,optionalBool: PropTypes.bool,optionalFunc: PropTypes.func,optionalNumber: PropTypes.number,optionalObject: PropTypes.object,optionalString: PropTypes.string,// 可以是一个ReactElement类型 optinalElement: PropTypes.element,// 可以是别的组件的实例 optinalMessage: PropTypes.instanceOf(Message),// 可以规定为一组值其中的一个 optinalEnum: PropTypes.oneOf(['News','Photos']),// 可以规定是一组类型中的一个 optionalUnion: PropTypes.oneOfType([ PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message) ]),// 可以在最后加一个isRequired,表明这个属性是必需的,否则就会返回一个错误 requiredFunc: React.PropTypes.func.isRequired } 给Profile组件添加验证: import React,{ PropTypes } from 'react'; // 需要验证属性 const propTypes = { name: PropTypes.string.isRequired,age: PropTypes.number.isRequired }; class Profile extends React.Component { // render是这个组件渲染的Vitrual DOM结构 render() { return ( <div className="profile-component"> <h1>我的名字叫{this.props.name}</h1> <h2>我今年{this.props.age}岁</h2> </div> ) } } // 将验证赋值给这个组件的propTypes属性 Profile.propTypes = propTypes; export default Profile; state状态state是组件内部属性。组件本身是一个状态机,它可以在constructor中通过this.state直接定义它的值,然后根据这些值来渲染不同的UI。当state值发生改变时,可以通过this.setState方法让组件再次调用render方法,来渲染新的UI。 // Profile.jsx export default class Profile extends React.Component { constructor(props) { super(props); this.state = { liked: 0 }; // ES6 class的组件声明方式,不会把自定义的函数绑定到实例上,需要手动绑定 this.likedCallback = this.likedCallback.bind(this); } } likedCallback() { let liked = this.state.liked; liked++; this.setState({ liked }); } render() { return ( <div> <h1>我的名字叫{this.props.name}</h1> <h2>我今年{this.props.age}岁</h2> <button onClick="this.likedCallback">给我点赞</button> <h2>总点赞数:{this.state.liked}</h2> </div> ) } 组件生命周期
1) 组件首次加载
2) 组件props更新
3) 组件卸载
组合组件一个组件可以包含多个其他组件,继续扩展一下上面的应用,显示一个爱好列表 // Hobby.jsx 爱好组件 import React,{ PropTypes } from 'react'; const propTypes = { hobby: PropTypes.string.isRequired }; class Hobby extends React.Component { render() { return <li>{this.props.hobby}</li> } } Hobby.propTypes = propTypes; export default Hobby; // Profile.jsx 使用爱好的组合组件 import Hobby from './hobby'; ... constructor(props) { super(props); this.state = { liked: 0,hobbies: ['skateboarding','rock music'] }; ... render() { return ( <div> <h1>我的名字叫{this.props.name}</h1> <h2>我今年{this.props.age}岁</h2> <button onClick="{this.likedCallback}">给我点赞</button> <h2>总点赞数:{this.state.liked}</h2> <h2>我的爱好:</h2> <ul> {this.state.hobbies.map((hobby,i) => <Hobby key={i} hobby={hobby} />)} </ul> </div> ) } } 只要将子组件看成自定义HTML标签就好了,传入想要的属性。特别注意要给每个循环组件添加一个唯一的key值。 无状态函数式组件Hobby这类组件,没有内部state,不需要组件生命周期函数。可以用纯函数的形式来表达。它做的事情只是根据输入来生成组件,没有其他副作用 // 无状态函数 function Hobby(props) { return <li>{props.hobby}</li> } state设计原则 创建尽量多的无状态组件,这些组件唯一关心的就是渲染数据。而在最外层,应该有一个包含state的父级别组件,用于处理各种事件、交流逻辑、修改state。对应的子组件要关心的只是传入的属性而已。 { goods: [ { "id": 1,"name": "paper" },{ "id": 2,"name": "pencil" },... ],selectedGoods: [ { "id": 1,"title": "hello world" } ] } 这样做当然可以,但是根据最小化设计state原则,selectedGoods的商品就是goods里的几项。所以可以修改成: selectedGoods: [1,2,3] 渲染时,只要把渲染的条目从goods中取出来就可以了。
DOM操作大多数情况下,不需要操作DOM去更新UI,应使用setState。但是有些情况确实需要访问一些DOM(如表达的值),那么可采用refs方式来获得DOM节点。只需要加个ref属性,然后通过this.refs.name来获得对应的DOM结构。 // Profile.jsx render() { return ( <div> ... <input type="text" ref="hobby" /> <button onClick={this.addHobbyCallback}>添加爱好</button> </div> ) } 在button上添加事件,取得input的值,添加到state的值里面: // Profile.jsx 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 = ''; }); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |