这次是用TodoList来总结一些react中的点,如vue中的v-model在react中要怎么做,组件之间怎么传值,属性校验等
TodoList小练习的页面如下:
state 、setState
首先,react中没有v-model,所以input中的数据双向绑定需要 state,onChange,setState来配合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import React from 'react';
class TodoList extends React.Component {
// 状态 state = { inpVal: '', list: ['蓝天', '白云', '青山'], count: 0 } handleChange = e => { this.setState({ inpVal: e.target.value }) }
handleClick = () => { this.setState({ list: [...this.state.list, this.state.inpVal], inpVal: '' }) }
handleDelete = index => { const list = this.state.list; list.splice(index, 1);
this.setState({ list }) } render() { return ( <> <div> <input type="text" value={ this.state.inpVal } onChange={ this.handleChange } /> <button onClick={ this.handleClick }>添加</button> </div> <ul> { this.state.list.map( (item, index) => ( <li key={ item }> { item } <button onClick={ () => { this.handleDelete(index) } }>X</button> </li> )) } </ul> </> ) } }
export default TodoList;
|
注意:
1 在绑定事件的时候,要注意this的情况,可以写onChange={ this.handleChange }
成 或者 用文中的这种箭头函数的方式
2 input绑定的是state中的值,所以刚开始没绑定onChange事件时会发现无法输入,因为state不能直接改变,需要通过setState这个方法改变state中的值,添加按钮也是同样的道理,list需要通过setState来改变
3 当需要传参时,使用 onClick={ () => { this.handleDelete(index) } }
, 而不是 onClick={ this.handleDelete(index) }
, 后者在绑定的时候就会执行,所以需要使用前者,当然也可以写成 bind(this
)
4 当使用map的方法时,需要加个key值来表明唯一性,尽量不要用index(因为当list的值发生变化,页面需要重新渲染的时候,react是根据key来和上一次的进行对比的,若key的这个元素依然)
现在有个问题,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| state = { count: 0 } <div> <span>{ this.state.count }</span> <button onClick={ this.handleAdd }>添加</button> </div> handleAdd = () =>{
//this.setState({ // count: 2 //}) this.setState((prevState) => { return { count: prevState.count + 1 } }) this.setState((prevState) => { return { count: prevState.count + 2 } }) this.setState((prevState) => { return { count: prevState.count + 3 } }) }
|
当点击添加按钮时,count 会加 6 吗,并没有,这里只加了 3 ,而这几句换个位置会发现,加的值变了,但始终不是 加 6 ,这是因为setState会有个事件队列,当对比发现改变的是同一个state属性时,会进行后面的覆盖前面的
受控组件、非受控组件
受控组件和非受控组件的区别就是:是否受状态影响
受控组件就是上面写的那种, input框的值受state中的影响的,所以是受控组件,如下:
1
| <input type="text" value={ this.state.inpVal } onChange={ this.handleChange } />
|
非受控组件就是给一个组件起了个名字,我们通过这个名字找到这个组件,然后去获取一些信息,如下:
1 2 3 4 5 6 7 8 9 10
|
<input type="text" ref={ (dom) => {this.inp = dom} } />
inp = React.createRef() <input type="text" ref={ this.inp } } />
|
属性校验
当一个组件传值给另一个组件时,需要以下几步:
父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const person = { name: '小安', age: 18, sex: '女', figure: { weight: 95, height: 168 }, hobby: ['看电影', '看书'], salary: 100 }
render(<Person {...person}></Person>, document.getElementById('root'))
|
子组件:
1 2 3 4 5 6
| const { name, age, sex, figure, hobby, salary } = this.props
<div>{name} 个人资料</div> <div>年龄:{ age }</div> <div>性别:{ sex }</div> ...
|
当然也可以设置默认属性,比如name这个属性,父组件有传 name,那就用父组件传过来的,没有那就用自己的,如下(子组件中):
1 2 3
| static defaultProps = { name: 'duyi' }
|
子组件通过 this.props
获取父组件传过来的值(props中的值是只读的),但当组件需要number而父组件传的是string 时,就需要属性校验了,属性校验需要以下几步:
1 安装 prop-types
:npm install prop-type --save
2 在子组件引入它:
1
| import PropTypes from 'prop-types';
|
3 定义所需要的属性各是什么类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static propTypes = { name: PropTypes.string, age: PropTypes.number, sex: PropTypes.oneOf(['男', '女']), figure: PropTypes.objectOf(PropTypes.number), hobby: PropTypes.arrayOf(PropTypes.string), salary (props, propsName, componentName) { if(props[propsName] < 10000) { return new Error( `${componentName}组件传递过来的${propsName}属性的值太小啦,应该大于1万` ); } } }
|
这样属性校验就好了
组件交互
还是以 这个TodoList为例,把这个功能分成了下面这样的几个组件:
父子组件之间的传值如前面那样,但如果跳级传值的话,一层一层传下去比较麻烦,所以react中可以通过content这种方式来传(content 是 16版本之后出现的 ),流程是下面这样:
而使用步骤是,把祖父组件用Provider
包裹起来,把孙子组件Consumer
包裹起来,具体步骤如下:
1 content 是react的,所以需要先引入, content.js:
1 2 3 4 5 6 7 8
| import React from 'react'
let {Provider, Consumer} = React.createContext();
export { Provider, Consumer }
|
2 TodoWrap.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| _handledelete = (index) => { const list = [...this.state.list]; list.splice(index, 1); this.setState({ list }); };
render() { return ( <Provider value={{ _handledelete: this._handledelete }} > <div> <input type="text" ref={this.task} /> <button onClick={this._handleClick}>添加</button> </div> <Todolistcomp list={this.state.list} fn={this._handledelete}/> </Provider> ); }
|
3 TodoItem.js, consumer中需要写成这种箭头函数的形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import React, { Component } from "react"; import { Consumer } from "./content";
class Todoitemcomp extends Component { _handledelete = index => { this.props._handledelete(index); }; render() { const { task, index } = this.props; return ( <Consumer> {({ _handledelete}) => ( <li> {task} <button onClick={() => _handledelete(index)}>X</button> </li> )} </Consumer> ); } }
export default Todoitemcomp;
|