前面的话

  React让组件化成为了前端开发的基本思路,比传统思路可以更好的控制前端复杂度,旧的开发方法受到了影响,如分离式的HTML/CSS、非侵入式JS、模板语言、MVC、CSS文件、Bootstrap等。在React中,组件把数据翻译成UI,数据通过组件props属性传入,组件自身状态通过state状态值来控制。 每个组件都是一个状态机,也就是声明式编程。数据有变化,组件自动刷新。本文将详细介绍React基本概念

JSX

  JSX是Javascript的语法扩展(extension),可以让我们在Javascript中可以编写像HTML一样的代码。

  JSX用来声明 React 当中的元素,JSX 中使用 JavaScript 表达式,JSX中的表达式要包含在大括号里

【模板字符串】

  可以在JSX中使用模板字符串

  1. {`Joined in ${time}`}

【属性】

  可以使用引号来定义以字符串为值的属性:

  1. const element = <div tabIndex=""></div>;

  也可以使用大括号来定义以 JavaScript 表达式为值的属性:

  1. const element = <img src={user.avatarUrl} />;

  下面这两个 JSX 表达式是等价的

  1. <MyComponent message="hello world" />
  2. <MyComponent message={'hello world'} />

【默认为true】

  如果没有给属性传值,它默认为 true

  1. <MyTextBox autocomplete />
  2. <MyTextBox autocomplete={true} />

【扩展属性】

  如果已经有了个 props 对象,并且想在 JSX 中传递它,可以使用 ... 作为扩展操作符来传递整个属性对象。下面两个组件是等效的:

  1. function App1() {
  2. return <Greeting firstName="Ben" lastName="Hector" />;
  3. }
  4.  
  5. function App2() {
  6. const props = {firstName: 'Ben', lastName: 'Hector'};
  7. return <Greeting {...props} />;
  8. }

【return】

  return一定要紧挨着左括号,否则不生效

【JSX是进步还是倒退】

  长期以来,一直不倡导在HTML中使用onclick,为什么在JSX中却要使用onClick这样的方式来添加事件处理函数呢?

  在React出现之初,很多人对React这样的设计非常反感,因为React把类似HTML的标记语言和Javascript混在一起了。但是,随着时间的推移,业界逐渐认可了这种方式,因为大家发现,以前用HTML来代表内容,用CSS代表样式,用Javascript来定义交互行为,这三种语言分在三种不同的文件里面,实际上是把不同技术分开管理了,而不是逻辑上的“分而治之”

  根据做同一件事的代码应该有高耦合性的设计原则,为什么不把实现这个功能的所有代码集中在一个文件里呢?

  在JSX中使用onClick来添加事件处理函数,是否代表网页应用开发兜了一个大圈,最终回到了起点呢?

  不是的,在HTML中直接使用onclick很不专业,因为onclick添加的事件处理函数是在全局环境下执行的,这污染了全局环境,很容易产生意料不到的后果;给很多DOM元素添加onclick事件,可能会影响网页的性能;对于使用onclick的DOM元素,如果在DOM元素删除后忘了注销事件处理函数,可能会造成内存泄漏

  上面说的这些问题在JSX中都不存在

  onClick挂载的每个函数,都可以控制在组件范围内,不会污染全局空间;在JSX中使用了onClick,但并没有产生直接使用onclick的HTML,而是使用事件委托的方式处理,无论多少个onclick出现,最后都只在DOM树上添加了一个事件处理函数,挂在最顶层的DOM节点上;因为React控制了组件的生命周期,在unmount时自然能够清除相关的所有事件处理函数,内存泄漏也不再是一个问题

样式设置

【行内样式】

  当属性的类型不是字符串类型时,在JSX中必须用花括号{}把prop值包住。所以style的值有两层花括号

  行内样式使用如下写法

  1. {{color:'red',backgroundColor:'blue'}}

【图片】

  图片的相对引用使用如下写法

  1. <img src={require('./common/img/128H.jpg')} alt="" />

【CSS引入】

  1. require('./common/style/main.css')

  或者

  1. import '@/assets/global.css'

【class设置】

  1. <div className="test"></div>

【自定义属性】

  1. <div data-abc=""></div>

组件

  作为软件设计的通用原则,组件的划分要满足高内聚和低耦合。高内聚是指把逻辑紧密相关的内容放在一个组件中。低耦合是指不同组件之间的依赖关系要尽量弱化,也就是每个组件要尽量独立

  组件从概念上看就像是函数,它可以接收任意的输入值(称之为“props”),并返回一个需要在页面上展示的React元素

  [注意]组件可以嵌套自身

【函数组件】

  定义一个组件最简单的方式是使用JavaScript函数

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }

【类组件】

  1. class Welcome extends React.Component {
  2. render() {
  3. return <h1>Hello, {this.props.name}</h1>;
  4. }
  5. }

prop

  当React遇到的元素是用户自定义的组件,它会将JSX属性作为单个对象传递给该组件,这个对象称之为“props”

  1. function Welcome(props) {
  2. return <h1>Hello, {props.name}</h1>;
  3. }
  4.  
  5. const element = <Welcome name="Sara" />;
  6. ReactDOM.render(
  7. element,
  8. document.getElementById('root')
  9. );

【只读性】

  无论是使用函数或是类来声明一个组件,它决不能修改它自己的props

【隐藏组件】

  让 render 方法返回 null 可以隐藏组件

【父传子】

  下面的例子来展示父级如何通过props把数据传递给子级

  1. class ControlPanel extends Component {
  2. render() {
  3. return (
  4. <div>
  5. <Counter caption="First"/>
  6. <Counter caption="Second" initValue={} />
  7. <Counter caption="Third" initValue={} />
  8. <button onClick={ () => this.forceUpdate() }>
  9. Click me to re-render!
  10. </button>
  11. </div>
  12. );
  13. }
  14. }

【读取props】

  下面的例子展示子级如何读取父级传递来的props

  1. class Counter extends Component {
  2.  
  3. constructor(props) {
  4. super(props);this.state = {
  5. count: props.initValue
  6. }
  7. }

【props检查】

  一个组件应该规范以下内容:这个组件支持哪些prop,以及每个prop应该是什么样的格式。React通过propTypes来支持这些功能

  1. Counter.propTypes = {
  2. caption: PropTypes.string.isRequired,
  3. initValue: PropTypes.number
  4. };
  5.  
  6. Counter.defaultProps = {
  7. initValue:
  8. };

【子传父】

  React组件要反馈数据在父组件时,可以使用prop。函数类型的prop等于让父组件交给子组件一个回调函数,子组件在恰当的时机调用函数类型的prop,可以带上必要的参数,这样就可以反过来把信息传递给父级

  下面的例子中,onUpdate是子组件向父组件传递数据的渠道

  1. //子组件
  2. class Counter extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
  6. this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
  7. this.state = {count: props.initValue}
  8. }
  9. onClickIncrementButton() {
  10. this.updateCount(true);
  11. }
  12. onClickDecrementButton() {
  13. this.updateCount(false);
  14. }
  15. updateCount(isIncrement) {
  16. const previousValue = this.state.count;
  17. const newValue = isIncrement ? previousValue + : previousValue - ;
  18. this.setState({count: newValue})
  19. this.props.onUpdate(newValue, previousValue)
  20. }
  21. render() {
  22. const {caption} = this.props;
  23. return (
  24. <div>
  25. <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
  26. <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
  27. <span>{caption} count: {this.state.count}</span>
  28. </div>
  29. );
  30. }
  31. }
  32. Counter.propTypes = {
  33. caption: PropTypes.string.isRequired,
  34. initValue: PropTypes.number,
  35. onUpdate: PropTypes.func
  36. };
  37. Counter.defaultProps = {
  38. initValue: ,
  39. onUpdate: f => f
  40. };
  41. export default Counter;
  1. //父组件
  2. class ControlPanel extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.onCounterUpdate = this.onCounterUpdate.bind(this);
  6. this.initValues = [ , , ];
  7. const initSum = this.initValues.reduce((a, b) => a+b, );
  8. this.state = {sum: initSum};
  9. }
  10. onCounterUpdate(newValue, previousValue) {
  11. const valueChange = newValue - previousValue;
  12. this.setState({ sum: this.state.sum + valueChange});
  13. }
  14. render() {
  15. return (
  16. <div>
  17. <Counter onUpdate={this.onCounterUpdate} caption="First" />
  18. <Counter onUpdate={this.onCounterUpdate} caption="Second" initValue={this.initValues[]} />
  19. <Counter onUpdate={this.onCounterUpdate} caption="Third" initValue={this.initValues[]} />
  20. <div>Total Count: {this.state.sum}</div>
  21. </div>
  22. );
  23. }
  24. }
  25. export default ControlPanel;

【局限】

  设想一下,在一个应用中,包含三级或三级以上的组件结构,顶层的祖父级组件想要传递一个数据给最低层的子组件,用prop的方式,就只能通过父组件中转,也许中间那一层根本用不上这个prop,但是依然需要支持这个prop,扮演好搬运工的角色,只因为子组件用得上,这明显违反了低耦合的设计要求。于是,提出了专门的状态管理的概念

State

  如何组织数据是程序的最重要问题。React组件的数据分为两种:prop和state。无论prop还是state的改变,都可能引发组件的重新渲染

  状态state与属性props十分相似,但是状态是私有的,完全受控于当前组件。prop是组件的对外接口,state是组件的内部状态

  由于React不能直接修改传入的prop,所以需要记录自身数据变化,就要使用state

【state与prop的区别】

  下面来总结下state与prop的区别

  1、prop用于定义外部接口,state用于记录内部状态

  2、prop的赋值在父组件使用该组件时,state的赋值在该组件内部

  3、组件不可修改prop的值,而state存在的目的就是让组件来改变的

  组件的state,相当于组件的记忆,其存在意义就是被改变,每一次通过this.setState函数修改state就改变了组件的状态,然后通过渲染过程把这种变化体现出来

【正确使用state】

  1、不要直接更新状态,构造函数是唯一能够初始化 this.state 的地方

  如果直接修改this.state的值,虽然事实上改变了组件的内部状态,但只是野蛮地修改了state,但没有驱动组件进行重新渲染。而this.setState()函数所做的事情,就是先改变this.state的值,然后驱动组件重新渲染

  1. // Wrong
  2. this.state.comment = 'Hello';
  3. // Correct
  4. this.setState({comment: 'Hello'});

  2、状态更新可能是异步的

  setState是异步更新,而不是同步更新,下面是一个例子

  1. setYear(){
  2. let {year} = this.state
  3. this.setState({
  4. year: year + //新值
  5. })
  6. console.log(this.state.year)//旧值
  7. }
  1. setYear(){
  2. setTimeout(() => {
  3. this.setState({
  4. year: year + //新值
  5. })
  6. console.log(this.state.year)//新值
  7. })
  8. }

  因为 this.props 和 this.state 可能是异步更新的,不应该依靠它们的值来计算下一个状态

  1. // Wrong
  2. this.setState({
  3. counter: this.state.counter + this.props.increment,
  4. });

  要修复它,要使用第二种形式的 setState() 来接受一个函数而不是一个对象。 该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数:

  1. // Correct
  2. this.setState((prevState, props) => ({
  3. counter: prevState.counter + props.increment
  4. }));

  3、状态更新合并

  可以调用 setState() 独立地更新它们,但React将多个setState() 调用合并成一个调用来提高性能。

  1. componentDidMount() {
  2. fetchPosts().then(response => {
  3. this.setState({
  4. posts: response.posts
  5. });
  6. });
  7.  
  8. fetchComments().then(response => {
  9. this.setState({
  10. comments: response.comments
  11. });
  12. });
  13. }

  这里的合并是浅合并,也就是说this.setState({comments})完整保留了this.state.posts,但完全替换了this.state.comments

  4、回调函数

  由于setState是异步更新的,如果需要确定setState更新后,再进行某些操作,可以使用setState的回调函数

  1. this.setState({
  2. val:value
  3. },() => {
  4. this.ref.editInput.focus()
  5. })

事件处理

  React 元素的事件处理和 DOM元素的很相似。但是有一点语法上的不同:

  1、React事件绑定属性的命名采用驼峰式写法,而不是小写

  2、如果采用 JSX 的语法需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素的写法)

  1. <button onClick={activateLasers}>
  2. Activate Lasers
  3. </button>

  [注意]在 React 中不能使用返回 false 的方式阻止默认行为。必须明确的使用 preventDefault

【绑定this】

  可以使用bind()方法

  1. this.handleClick = this.handleClick.bind(this);

  也可以使用属性初始化器语法

  1. handleClick = () => {
  2. console.log('this is:', this);
  3. }

  如果没有使用属性初始化器语法,可以在回调函数中使用箭头函数

  1. class LoggingButton extends React.Component {
  2. handleClick() {
  3. console.log('this is:', this);
  4. }
  5. render() {
  6. return (
  7. <button onClick={(e) => this.handleClick(e)}>
  8. Click me
  9. </button>
  10. );
  11. }
  12. }

  使用这个语法有个问题就是每次 LoggingButton 渲染的时候都会创建一个不同的回调函数。在大多数情况下,这没有问题。然而如果这个回调函数作为一个属性值传入低阶组件,这些组件可能会进行额外的重新渲染。通常建议在构造函数中绑定或使用属性初始化器语法来避免这类性能问题

【传递参数】

  以下两种方式都可以向事件处理程序传递参数:

  1. <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
  2. <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

  [注意]通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面

  1. class Popper extends React.Component{
  2. preventPop(name, e){
  3. e.preventDefault();
  4. alert(name);
  5. }
  6. render(){
  7. return (<a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
  8. );
  9. }
  10. }

【原生事件对象】

  1. handleClick(e){
  2. e.nativeEvent
  3. }

列表

【keys】

  Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此应当给数组中的每一个元素赋予一个确定的标识

  1. const numbers = [, , , , ];
  2. const listItems = numbers.map((number) =>
  3. <li key={number.toString()}>
  4. {number}
  5. </li>
  6. );

  一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串。通常,使用来自数据的id作为元素的key

  1. const todoItems = todos.map((todo) =>
  2. <li key={todo.id}>
  3. {todo.text}
  4. </li>
  5. );

  当元素没有确定的id时,可以使用序列号索引index作为key

  1. const todoItems = todos.map((todo, index) =>
  2. <li key={index}>
  3. {todo.text}
  4. </li>
  5. );

  [注意]如果列表可以重新排序,不建议使用索引来进行排序,因为这会导致渲染变得很慢

  JSX允许在大括号中嵌入任何表达式

  1. function NumberList(props) {
  2. const numbers = props.numbers;
  3. return (
  4. <ul>
  5. {numbers.map((number) =>
  6. <ListItem key={number.toString()}
  7. value={number} />
  8.  
  9. )}
  10. </ul>
  11. );
  12. }

表单

【受控组件】

  在HTML当中,像<input>,<textarea>, 和 <select>这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新

  通过使react变成一种单一数据源的状态来结合二者。React负责渲染表单的组件仍然控制用户后续输入时所发生的变化。相应的,其值由React控制的输入表单元素称为“受控组件”

  1. class NameForm extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {value: ''};
  5.  
  6. this.handleChange = this.handleChange.bind(this);
  7. this.handleSubmit = this.handleSubmit.bind(this);
  8. }
  9.  
  10. handleChange(event) {
  11. this.setState({value: event.target.value});
  12. }
  13.  
  14. handleSubmit(event) {
  15. alert('A name was submitted: ' + this.state.value);
  16. event.preventDefault();
  17. }
  18.  
  19. render() {
  20. return (
  21. <form onSubmit={this.handleSubmit}>
  22. <label>
  23. Name:
  24. <input type="text" value={this.state.value} onChange={this.handleChange} />
  25. </label>
  26. <input type="submit" value="Submit" />
  27. </form>
  28. );
  29. }
  30. }

  由于 value 属性是在表单元素上设置的,因此显示的值将始终为 React数据源上this.state.value 的值。由于每次按键都会触发 handleChange 来更新当前React的state,所展示的值也会随着不同用户的输入而更新

【textarea】

  在HTML当中,<textarea> 元素通过子节点来定义它的文本内容。在React中,<textarea>会用value属性来代替。这样的话,表单中的<textarea> 非常类似于使用单行输入的表单:

  1. <textarea value={this.state.value} onChange={this.handleChange} />

【select】

  在React中,并不使用之前的selected属性,而在根select标签上用value属性来表示选中项。这在受控组件中更为方便,因为只需要在一个地方来更新组件

  1. <select value={this.state.value} onChange={this.handleChange}>
  2. <option value="grapefruit">Grapefruit</option>
  3. <option value="lime">Lime</option>
  4. </select>

【多个input】

  有处理多个受控的input元素时,可以通过给每个元素添加一个name属性,来让处理函数根据 event.target.name的值来选择做什么

  1. class Reservation extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. isGoing: true,
  6. numberOfGuests:
  7. };
  8. this.handleInputChange = this.handleInputChange.bind(this);
  9. }
  10.  
  11. handleInputChange(event) {
  12. const target = event.target;
  13. const value = target.type === 'checkbox' ? target.checked : target.value;
  14. const name = target.name;
  15. this.setState({
  16. [name]: value
  17. });
  18. }
  19.  
  20. render() {
  21. return (
  22. <form>
  23. <label>
  24. Is going:
  25. <input
  26. name="isGoing"
  27. type="checkbox"
  28. checked={this.state.isGoing}
  29. onChange={this.handleInputChange} />
  30. </label>
  31. <br />
  32. <label>
  33. Number of guests:
  34. <input
  35. name="numberOfGuests"
  36. type="number"
  37. value={this.state.numberOfGuests}
  38. onChange={this.handleInputChange} />
  39. </label>
  40. </form>
  41. );
  42. }
  43. }

propTypes

  要检查组件的属性,需要配置特殊的 propTypes 属性

  1. import PropTypes from 'prop-types';
  2.  
  3. class Greeting extends React.Component {
  4. render() {
  5. return (
  6. <h1>Hello, {this.props.name}</h1>
  7. );
  8. }
  9. }
  10.  
  11. Greeting.propTypes = {
  12. name: PropTypes.string
  13. };

  react支持如下验证

  1. import PropTypes from 'prop-types';
  2.  
  3. MyComponent.propTypes = {
  4. // 可以将属性声明为以下 JS 原生类型
  5. optionalArray: PropTypes.array,
  6. optionalBool: PropTypes.bool,
  7. optionalFunc: PropTypes.func,
  8. optionalNumber: PropTypes.number,
  9. optionalObject: PropTypes.object,
  10. optionalString: PropTypes.string,
  11. optionalSymbol: PropTypes.symbol,
  12.  
  13. // 任何可被渲染的元素(包括数字、字符串、子元素或数组)。
  14. optionalNode: PropTypes.node,
  15.  
  16. // 一个 React 元素
  17. optionalElement: PropTypes.element,
  18.  
  19. // 也可以声明属性为某个类的实例
  20. optionalMessage: PropTypes.instanceOf(Message),
  21.  
  22. // 也可以限制属性值是某个特定值之一
  23. optionalEnum: PropTypes.oneOf(['News', 'Photos']),
  24.  
  25. // 限制它为列举类型之一的对象
  26. optionalUnion: PropTypes.oneOfType([
  27. PropTypes.string,
  28. PropTypes.number,
  29. PropTypes.instanceOf(Message)
  30. ]),
  31.  
  32. // 一个指定元素类型的数组
  33. optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
  34.  
  35. // 一个指定类型的对象
  36. optionalObjectOf: PropTypes.objectOf(PropTypes.number),
  37.  
  38. // 一个指定属性及其类型的对象
  39. optionalObjectWithShape: PropTypes.shape({
  40. color: PropTypes.string,
  41. fontSize: PropTypes.number
  42. }),
  43.  
  44. // 也可以在任何 PropTypes 属性后面加上 `isRequired` 后缀
  45. requiredFunc: PropTypes.func.isRequired,
  46.  
  47. // 任意类型的数据
  48. requiredAny: PropTypes.any.isRequired,
  49.  
  50. // 也可以指定一个自定义验证器。它应该在验证失败时返回
  51. // 一个 Error 对象而不是 `console.warn` 或抛出异常。
  52. // 不过在 `oneOfType` 中它不起作用。
  53. customProp: function(props, propName, componentName) {
  54. if (!/matchme/.test(props[propName])) {
  55. return new Error(
  56. 'Invalid prop `' + propName + '` supplied to' +
  57. ' `' + componentName + '`. Validation failed.'
  58. );
  59. }
  60. },
  61.  
  62. // 可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器,它应该在验证失败时返回一个 Error 对象。 它被用于验证数组或对象的每个值。验证器前两个参数的第一个是数组或对象本身,第二个是它们对应的键。
  63. customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
  64. if (!/matchme/.test(propValue[key])) {
  65. return new Error(
  66. 'Invalid prop `' + propFullName + '` supplied to' +
  67. ' `' + componentName + '`. Validation failed.'
  68. );
  69. }
  70. })
  71. };

【限制单个子代】

  使用 PropTypes.element 可以指定只传递一个子代

  1. import PropTypes from 'prop-types';
  2.  
  3. class MyComponent extends React.Component {
  4. render() {
  5. const children = this.props.children;
  6. return (
  7. <div>
  8. {children}
  9. </div>
  10. );
  11. }
  12. }
  13.  
  14. MyComponent.propTypes = {
  15. children: PropTypes.element.isRequired
  16. };

【属性默认值】

  可以通过配置 defaultProps 为 props定义默认值

  1. class Greeting extends React.Component {
  2. render() {
  3. return (
  4. <h1>Hello, {this.props.name}</h1>
  5. );
  6. }
  7. }
  8.  
  9. // 为属性指定默认值:
  10. Greeting.defaultProps = {
  11. name: 'Stranger'
  12. };
  13.  
  14. // 渲染 "Hello, Stranger":
  15. ReactDOM.render(
  16. <Greeting />,
  17. document.getElementById('example')
  18. );

返回多个元素

  React 中一个常见模式是为一个组件返回多个元素。Fragments 可以让你聚合一个子元素列表,并且不在DOM中增加额外节点

  Fragments 看起来像空的 JSX 标签:

  1. render() {
  2. return (
  3. <>
  4. <ChildA />
  5. <ChildB />
  6. <ChildC />
  7. </>
  8. );
  9. }

  [注意]<></> 语法不能接受键值或属性

  另一种使用片段的方式是使用 React.Fragment 组件,React.Fragment 组件可以在 React 对象上使用,<></> 是 <React.Fragment/> 的语法糖

  1. class Columns extends React.Component {
  2. render() {
  3. return (
  4. <React.Fragment>
  5. <td>Hello</td>
  6. <td>World</td>
  7. </React.Fragment>
  8. );
  9. }
  10. }

  如果需要一个带 key 的片段,可以直接使用 <React.Fragment /> 。 一个使用场景是映射一个集合为一个片段数组 — 例如:创建一个描述列表:

  1. function Glossary(props) {
  2. return (
  3. <dl>
  4. {props.items.map(item => (
  5. <React.Fragment key={item.id}>
  6. <dt>{item.term}</dt>
  7. <dd>{item.description}</dd>
  8. </React.Fragment>
  9. ))}
  10. </dl>
  11. );
  12. }

  [注意]如果使用create-react-app构建的项目,不支持<></>,但支持<React.Fragment />的形式

context

  在嵌套层级较深的场景中,不想要向下每层都手动地传递需要的 props。这就需要强大的 context API了。其中,react-redux中的provider组件就是使用context实现的

【手动传递props】

  下面是手动传递props的例子

  1. class Button extends React.Component {
  2. render() {
  3. return (
  4. <button style={{background: this.props.color}}>
  5. {this.props.children}
  6. </button>
  7. );
  8. }
  9. }
  10.  
  11. class Message extends React.Component {
  12. render() {
  13. return (
  14. <div>
  15. {this.props.text} <Button color={this.props.color}>Delete</Button>
  16. </div>
  17. );
  18. }
  19. }
  20.  
  21. class MessageList extends React.Component {
  22. render() {
  23. const color = "purple";
  24. const children = this.props.messages.map((message) =>
  25. <Message text={message.text} color={color} />
  26. );
  27. return <div>{children}</div>;
  28. }
  29. }

【使用context】

  下面使用context来自动传递

  通过在MessageList(context提供者)中添加childContextTypes和getChildContext,React会向下自动传递参数,任何组件只要在它的子组件中(这个例子中是Button),就能通过定义contextTypes来获取参数。

  1. const PropTypes = require('prop-types');
  2. class Button extends React.Component {
  3. render() {
  4. return (
  5. <button style={{background: this.context.color}}>
  6. {this.props.children}
  7. </button>
  8. );
  9. }
  10. }
  11. Button.contextTypes = {
  12. color: PropTypes.string
  13. };
  14.  
  15. class Message extends React.Component {
  16. render() {
  17. return (
  18. <div>
  19. {this.props.text} <Button>Delete</Button>
  20. </div>
  21. );
  22. }
  23. }
  24.  
  25. class MessageList extends React.Component {
  26. getChildContext() {
  27. return {color: "purple"};
  28. }
  29. render() {
  30. const children = this.props.messages.map((message) =>
  31. <Message text={message.text} />
  32. );
  33. return <div>{children}</div>;
  34. }
  35. }
  36. MessageList.childContextTypes = {
  37. color: PropTypes.string
  38. };

  [注意]如果contextTypes没有定义,那么context将会是个空对象

【生命周期】

  如果一个组件中定义了contextTypes,那么下面这些生命周期函数将会接收到额外的参数,即context对象

  1. constructor(props, context)
  2. componentWillReceiveProps(nextProps, nextContext)
  3. shouldComponentUpdate(nextProps, nextState, nextContext)
  4. componentWillUpdate(nextProps, nextState, nextContext)
  5. componentDidUpdate(prevProps, prevState, prevContext)

【无状态组件】

  如果contextTypes作为函数参数被定义的话,无状态函数组件也是可以引用context。以下代码展示了用无状态函数组件写法的Button组件

  1. const PropTypes = require('prop-types');
  2.  
  3. const Button = ({children}, context) =>
  4. <button style={{background: context.color}}>
  5. {children}
  6. </button>;
  7.  
  8. Button.contextTypes = {color: PropTypes.string};

获取尺寸

  如果在react中获取尺寸,可以使用offset、getBoudingClientRect()等原生JS的尺寸属性

  1. e.target.offsetHeight

React简明学习的更多相关文章

  1. WebPack 简明学习教程

    WebPack 简明学习教程 字数1291 阅读22812 评论11 喜欢35 WebPack是什么 一个打包工具 一个模块加载工具 各种资源都可以当成模块来处理 网站 http://webpack. ...

  2. (转)2019年 React 新手学习指南 – 从 React 学习线路图说开去

    原文:https://www.html.cn/archives/10111 注:本文根据 React 开发者学习线路图(2018) 结构编写了很多新手如何学习 React 的建议.2019 年有标题党 ...

  3. React Native 学习-01

    React Native 学习 (学习版本 0.39) 一.环境配置 二.IDE选择 webstorm 1.webstorm配置 ①.首先是可以选择使用汉化包汉化.eu68 ②.安装插件和外部库. 由 ...

  4. 【学】React的学习之旅1

    React的学习之旅1 单标签要有斜杠代表结束 用React.createClass()方法时,赋值后的组件名称首字母一定要大写 一定要先定义组件,再用ReactDOM.render调用 组件里ren ...

  5. React Ntive 学习手记

    React使今年来比较热门的前端库,之所以说是库呢,因为React.js是应用于MVC中的V层, 它并不是一个完整的MVC框架,所以,我也不知称之为框架了. 不过这并不影响React的火热. 混合应用 ...

  6. react native 学习一(环境搭配和常见错误的解决)

    react native 学习一(环境搭配) 首页,按照http://reactnative.cn/docs/0.30/getting-started.html#content上的介绍,下载安装pyt ...

  7. React.js学习

    React.js学习之环境搭建 1 工欲善其事必先利其器:前端开发工具 1.1 WebStorm和Sublime Text Sublime Text:作为代码编辑器,Sublime Text的优点如下 ...

  8. React 入门学习笔记整理目录

    React 入门学习笔记整理(一)--搭建环境 React 入门学习笔记整理(二)-- JSX简介与语法 React 入门学习笔记整理(三)-- 组件 React 入门学习笔记整理(四)-- 事件 R ...

  9. React Native 学习资料

    React Native 学习资料 学习资料 网址 React Native中文网 https://reactnative.cn/

随机推荐

  1. ubuntu10.04 安装配置tftp服务

    tftpd-hpa 是一个功能增强的TFTP服务器.它提供了很多TFTP的增强功能,它已经被移植到大多数的现代UNIX系统. 1.安装 sudo apt-get install tftpd-hpa t ...

  2. eclipse中maven的run as打war包失败的问题

    场景一: 由于某些原因,有的时候需要暂时在断网的情况下,或者更标准的说,是在连不上公司的maven公有仓库的情况下打包. 很长一段时间,我打包都是在eclipse中用run as在线打包,直到前不久一 ...

  3. WIN7 嵌入式系统安装教程 Windows Embedded Standard 2011 安装

    轻松构建你的第一个 Windows Embedded Standard 2011 镜像.通过本文你可以快速掌握如何使用Windows Embedded Standard 2011 CTP1 来构建一个 ...

  4. Java获取某年某月的第一天

    Java获取某年某月的第一天 1.设计源码 FisrtDayOfMonth.java: /** * @Title:FisrtDayOfMonth.java * @Package:com.you.fre ...

  5. TypeError: Error #1034: 强制转换类型失败:无法将 "" 转换为 Array。

    1.错误描述 TypeError: Error #1034: 强制转换类型失败:无法将 "" 转换为 Array. at mx.charts.series::LineSeries/ ...

  6. 芝麻HTTP:设置Selenium+Chrome代理

    微博登录限制了错误次数···加上Cookie大批账号被封需要从Cookie池中 剔除被封的账号··· 需要使用代理··· 无赖百度了大半天都是特么的啥玩意儿???结果换成了 Google手到擒来 分分 ...

  7. img 标签 访问图片 返回403 forbidden问题

    之前在项目里,本地调试的时候,图片src引用了第三方网站的图片资源,导致控制台出现了如下的报错: 403 forbidden,说明了这个网络资源这样获取是被拒绝的,那么通过简单的百度,找到了相关的解决 ...

  8. 如何使用jQuery-ContextMenu实现右击菜单

    最近在做项目中,遇到一个棘手的问题,页面上有很多功能需要实现,每个功能需要绑定一个按钮.如果一个功能绑定一个按钮,那么将会占用页面很大的空间,而且可能会使页面变得不美观.思前想后,决定将所有按钮做成右 ...

  9. 浅谈java编译机制和运行机制

    源文件和字节码的组成方式 源文件: 拓展名后跟java的文件即java的源文件. Java 源码编译由以下三个过程组成: 1.分析和输入到符号表 2.注解处理 3.语义分析和生成class文件 流程图 ...

  10. 零基础新手学习Java必须知道的市场行情

    Java如今的市场不如从前,竞争很大,工资非常高,标准非常高,想要胜任一份高薪的工作不是那么容易,只有掌握最新的行情才能更好的了解Java,才能更好的在这个领取发展,让新手小白了解Java市场行情如下 ...