react----自定义UI组件(表单)


react组件

react组件的后缀名可以是js,也可以是jsx

需要配置webpack的解析器以及缺省的后缀名
531b3b359fdff69bd63191f101fbbd99.png
8b80f356bd25badd77c9a7ae5fc61cf3.png

重新调整代码结构

src/index.js
dd460f1f3866c5b14ac4c593cc705fa3.png
85a49372f87d6823084dacfc985d45d5.png

React props

state 和 props 主要区别在于 props 是不可变的,而 state 可以根据用户的交互来改变。

组件本身自己用state, 组件之间使用props(vue中组件本身data,组件之间传值)

props的基本案例

父子组件的传值

父组件在调用子组件的地方,添加一个自定义的属性,属性的值就是你需要传递给子组件的值,如果属性的值是变量,boolean,number,需要使用 {}包裹

在子组件中 通过 this.props 获取到父组件传递过来的数据

import React from 'react';

class Child extends React.Component {
  render () {
    console.log(this.props)
    { this.props.test }
    return (
      <div>
        <h2>props</h2>
      </div>
    )
  }
}

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
    }
  }

  render () {
    return (
      <div>
        <h2>react组件之间传值</h2>
        <Child test="测试" />
      </div>
    )
  }
}

export default App;

如果传递的是一个变量

import React from 'react';

class Child extends React.Component {
  render () {
    console.log(this.props)
    { this.props.test }
    return (
      <div>
        <h2>props</h2>
        {
            //-------------------新增代码---------------------------
        }
        {
            this.props.list.map((item, index) => (
                <p key={ index }>{ item }</p>
            ))
        }
        {
            //-------------------新增代码---------------------------
        }
      </div>
    )
  }
}

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
        list: ['HTML5', 'JAVAEE', 'PYTHON', '物联网', '测试', '大数据', '云计算', 'UI']
    }
  }

  render () {
    return (
      <div>
        <h2>react组件之间传值</h2>
        <Child test="测试" list={ this.state.list }/>
      </div>
    )
  }
}

export default App;

如果props需要要默认值呢(传则用传的,不传则用默认的)

import React from 'react';

class Child extends React.Component {
  render () {
    console.log(this.props)
    { this.props.test }
    return (
      <div>
        <h2>props</h2>
        {
            this.props.list.map((item, index) => (
                <p key={ index }>{ item }</p>
            ))
        }
        {
            //-------------------新增代码---------------------------
        }
        {
            this.props.name
        }
        {
            //-------------------新增代码---------------------------
        }
      </div>
    )
  }
}

// ------------------新增代码---------------------------

Child.defaultProps = {
    name: '我是默认的属性'
}

// ------------------新增代码---------------------------

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
        list: ['HTML5', 'JAVAEE', 'PYTHON', '物联网', '测试', '大数据', '云计算', 'UI']
    }
  }

  render () {
    return (
      <div>
        <h2>react组件之间传值</h2>
        <Child test="测试" list={ this.state.list }/>
      </div>
    )
  }
}

export default App;

如果传递的值需要 数据类型的校验

cnpm i prop-types -D

(React v15.5 版本后把数据类型检验单独的移动到 npm 包,减少核心库的体积)

import React from 'react';
// -----------------新增代码---------------------------import PropTypes from 'prop-types';
class Child extends React.Component {
  render () {
    console.log(this.props)
    { this.props.test }
    return (
      <div>
        <h2>props</h2>
        {
            this.props.list.map((item, index) => (
                <p key={ index }>{ item }</p>
            ))
        }
        {
            this.props.name
        }
      </div>
    )
  }
}
Child.defaultProps = {
    name: '我是默认的属性'
}
// ------------------新增代码---------------------------


// 验证方式 Child.propTypes 注意大小写
Child.propTypes = {
    name: PropTypes.string,
    test: PropTypes.string,
    list: PropTypes.array
}
// ------------------新增代码---------------------------

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
        list: ['HTML5', 'JAVAEE', 'PYTHON', '物联网', '测试', '大数据', '云计算', 'UI']
    }
  }

  render () {
    return (
      <div>
        <h2>react组件之间传值</h2>
        <Child test="测试" list={ this.state.list }/>
      </div>
    )
  }
}

export default App;

拓展-react15.5以前组件写法以及验证

// 这段代码不要在编辑器编写,编写无效 -- react版本var title = 'hello react';
var MyTitle = React.createClass({
    propTypes: {
        title: React.PropTypes.string.isRequired
    },
    render () {
        return (
            <div>{ this.props.title }</div>
        )
    }
})
ReactDOM.render(<MyTitle title={ tilte } />, document.getElementById('app'))

.
react props 验证规则
https://react.docschina.org/docs/typechecking-with-proptypes.html

import PropTypes from 'prop-types';

MyComponent.propTypes = {
  // 你可以将属性声明为 JS 原生类型,默认情况下
  // 这些属性都是可选的。
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 任何可被渲染的元素(包括数字、字符串、元素或数组)
  // (或 Fragment) 也包含这些类型。
  optionalNode: PropTypes.node,

  // 一个 React 元素。
  optionalElement: PropTypes.element,

  // 一个 React 元素类型(即,MyComponent)。
  optionalElementType: PropTypes.elementType,

  // 你也可以声明 prop 为类的实例,这里使用
  // JS 的 instanceof 操作符。
  optionalMessage: PropTypes.instanceOf(Message),

  // 你可以让你的 prop 只能是特定的值,指定它为
  // 枚举类型。
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 一个对象可以是几种类型中的任意一个类型
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 可以指定一个数组由某一类型的元素组成
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 可以指定一个对象由某一类型的值组成
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 可以指定一个对象由特定的类型值组成
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),
  
  // An object with warnings on extra properties
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),   

  // 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
  // 这个 prop 没有被提供时,会打印警告信息。
  requiredFunc: PropTypes.func.isRequired,

  // 任意类型的数据
  requiredAny: PropTypes.any.isRequired,

  // 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
  // 请不要使用 `console.warn` 或抛出异常,因为这在 `onOfType` 中不会起作用。
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
  // 它应该在验证失败时返回一个 Error 对象。
  // 验证器将验证数组或对象中的每个值。验证器的前两个参数
  // 第一个是数组或对象本身
  // 第二个是他们当前的键。
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })};

React 事件处理

react元素的事件处理和DOM元素类似,有一点语法的不同

  • react事件绑定采用驼峰式命名,而不是小写
  • 如果采用jsx语法 需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素写法)

DOM

<button onclick="fn()"></button>

React

<button onClick={ this.fn }></button>
<button onClick={ () => {} }></button>
  • 构造方法改变this -- 给组件的实例添加新的方法
import React from 'react';
class App extends React.Component {
  constructor (props) {
    super(props);
    // 这里绑定时必须的,这样 this 才能在回调函数中使用
    // 给当前的组件添加了一个实例方法, 这个类中定义了一个方法,改变了this指向
    this.hanleClickFn = this.hanleClick.bind(this)
    this.state = {
      isToggleOn: true // 默认按钮为开
    }
  }
  // 必须小心对待jsx中的this,类的方法默认不会绑定当前组件的this
  // 可以在构造方法将自定义的函数绑定到当前的组件实例中
  hanleClick () {
    console.log('按钮被点击了', this)
    // 改变react状态   this.setState({ key: val })
    // react 改变状态   获取状态   处理状态  修改数据
    // 不能使用 this.state.isToggleOn = !this.state.isToggleOn 违背了react的原则
    let isToggleOn = this.state.isToggleOn
    isToggleOn = !isToggleOn
    // 状态的改变会引起视图的二次渲染
    this.setState({
      isToggleOn: isToggleOn
    })
  }
  render () {
    return (
      <div>
        <h2>事件处理</h2>
        {/* 点击以下按钮实现按钮的开关 */}
        <button onClick= { this.hanleClickFn }>
          { this.state.isToggleOn ? '开' : '关' }
        </button>
      </div>
    )
  }
}

export default App;

710f4b6846731b46438e0c57498806a0.png

  • 直接改变this指向 - 不会给组件实例添加新的方法 (推荐)
import React from 'react';
class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      isToggleOn: true // 默认按钮为开
    }
  }
  hanleClick () {
    console.log('按钮被点击了', this)
    let isToggleOn = this.state.isToggleOn
    isToggleOn = !isToggleOn
    this.setState({
      isToggleOn
    })
  }
  render () {
    return (
      <div>
        <h2>事件处理</h2>
        {/* 点击以下按钮实现按钮的开关 */}
        <button onClick= { this.hanleClick.bind(this) }>
          { this.state.isToggleOn ? '开' : '关' }
        </button>
      </div>
    )
  }
}

export default App;

  • 传入函数作为事件处理函数 --- (推荐)
import React from 'react';
class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      isToggleOn: true // 默认按钮为开
    }
  }
  render () {
    return (
      <div>
        <h2>事件处理</h2>
        {/* 点击以下按钮实现按钮的开关 */}
        <button onClick= { () => {
          console.log(this)
          let isToggleOn = this.state.isToggleOn
          isToggleOn = !isToggleOn
          this.setState({
            isToggleOn
          })
        } }>
          { this.state.isToggleOn ? '开' : '关' }
        </button>
      </div>
    )
  }
}

export default App;

绑定事件传递参数

  • 传递事件对象参数
import React from 'react';
class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      isToggleOn: true // 默认按钮为开
    }
  }

  getEvent (event) {
    console.log(event)
  }

  render () {
    return (
      <div>
        <h2>事件处理</h2>
        {/* 点击以下按钮实现按钮的开关 */}
        <button onClick= { this.getEvent }>获取事件对象</button>
        <button onClick= { this.getEvent.bind(this) }>获取事件对象2</button>

        <button onClick= { (event) => {
          console.log(event)
          let isToggleOn = this.state.isToggleOn
          isToggleOn = !isToggleOn
          this.setState({
            isToggleOn
          })
        } }>
          { this.state.isToggleOn ? '开' : '关' }
        </button>
      </div>
    )
  }
}

export default App;

  • 传递自定义的参数
import React from 'react';
class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      isToggleOn: true // 默认按钮为开
    }
  }

  // 事件对象参数在最后, 因为不一定会用到事件对象
  // getEvent (params, event) {
  //   console.log(params)
  //   console.log(event)
  // }

  // 事件对象不是必须的参数
  getEvent (params) {
    console.log(params)
    console.log(event)
  }

  getEvent1 (params1, params2) {
    console.log(params1, params2)
  }
  render () {
    let a = 10
    return (
      <div>
        <h2>事件处理</h2>
        {/* 点击以下按钮实现按钮的开关 */}
        <button onClick= { this.getEvent.bind(this, '哈哈哈') }>传递普通的值</button>
        <button onClick= { this.getEvent1.bind(this, '哈哈哈', '啦啦啦') }>传递普通的值</button>

        <button onClick = { () => {
          console.log(a)
          console.log(event)
        }}>箭头函数传值</button>

        <button onClick= { (event) => {
          console.log(event)
          let isToggleOn = this.state.isToggleOn
          isToggleOn = !isToggleOn
          this.setState({
            isToggleOn
          })
        } }>
          { this.state.isToggleOn ? '开' : '关' }
        </button>
      </div>
    )
  }
}

export default App;

React表单

表单元素会保留一些内部状态

import React from 'react';
class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      username: '',
      password: ''
    }
  }

  handleChange (event) {
    console.log(event.target.value)
    this.setState({
      username: event.target.value
    })
  }

  render () {
    return (
      <div>
        <input type="text" value = { this.state.username } onChange = { this.handleChange.bind(this) }/>
        <input type="password" value={ this.state.password }onChange = { (event) => {
          this.setState({
            password: event.target.value
          })
        } }/>
      </div>
    )
  }
}

export default App;

  • 假设表单组件在子组件中

需要在父组件通过事件句柄,并且作为属性传递到子组件中

封装UI库之-输入框组件

属性 描述 默认值 是否必传
type 类型 ‘text'
placeholder 占位符 ‘'
value 输入框的值 ‘'
error 提示信息 ‘'
clearable 清除按钮 false
事件 描述 默认值 是否必传
onChange 输入框值发生变化  
import React from 'react';

class MyInput extends React.Component {
    render () {
        return (
        <div>
            <input
                type={this.props.type || 'text'}
                placeholder = { this.props.placeholder || '' }
                value = { this.props.value }
                onChange = { this.props.onChange }
            />
            {
                this.props.clearable && this.props.value.length > 0 ? <span style = { { display: 'inline-block', width: '20px', height: '20px', background: '#333', color: '#fff', borderRadius: '50%', textAlign: 'center' } }>×</span> : ''
            }
            { this.props.error || ''}
        </div>
        )
    }
}

调用组件

import React from 'react';

class MyInput extends React.Component {
    constructor (props) {
        super(props);
        this.state = {
            username: '',
            usernametip: '',
            tel: '',
            teltip: '',
            password: '',
            passwordtip: '',
        }
    }
    usernameChange () {
        this.setState({
            username: event.target.value
        })
    }
    
    passwordChange () {
        this.setState({
            password: event.target.value
        })
    }
    render () {
        return (
            <div>
                { /* 基本用法 */ }
                <MyInput 
                    value = { this.state.username }
                    onChange = { this.usernameChange.bind(this) }
                />
                 { /* 添加密码,占位符 */ }
                <MyInput 
                    type="password"
                    placeholder="请输入密码"
                    value = { this.state.password }
                    onChange = { this.passwordChange.bind(this) }
                />
                 { /* 添加密码,占位符,添加一个清除按钮 */ }
                <MyInput 
                    type="password"
                    placeholder="请输入密码"
                    value = { this.state.password }
                    onChange = { this.passwordChange.bind(this) }
                    clearable
                />
                 { /* 添加密码,占位符,添加一个清除按钮, 添加错误提交信息*/ }
                <MyInput 
                    type="password"
                    placeholder="请输入密码"
                    value = { this.state.password }
                    onChange = { this.passwordChange.bind(this) }
                    error = { this.state.usernametip }
                    clearable
                />
            </div>
        )
    }
}

react版本TodoList

  • 表单和列表在一个组件
import React from 'react';

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      list: [],
      username: ''
    }
  }
  getUsername () {
    this.setState({
      username: event.target.value
    })
  }
  addUser () {
    const list = this.state.list
    list.push(this.state.username)
    this.setState({
      list
    })
  }
  render () {
    return (
      <div>
        <input type="text" value={ this.state.username } onChange = { this.getUsername.bind(this) }/>
        <button onClick={ this.addUser.bind(this) }>添加</button>
        <ul>
          {
            this.state.list.length > 0 ? this.state.list.map((item, index) => {
              return (
                <li key={ index }>
                  { item }
                  <button onClick = { () => {
                    const list = this.state.list
                    list.splice(index, 1)
                    this.setState({ 
                      list
                    })
                  } }>删除</button>
                </li>
              )
            }) : <li>暂无数据</li>
          }
        </ul>
      </div>
    )
  }
}

export default App;

  • 表单在父组件,列表在子组件
import React from 'react';

// 函数式组件不可以访问this,好友默认的参数为propsconst List = (props) => (
  <ul>
    {
      props.list.length > 0 ? props.list.map((item, index) => {
        return (
          <li key={ index }>
            { item }
            <button onClick = { () => {
              props.deleteItem(index)
            } }>删除</button>
          </li>
        )
      }) : <li>暂无数据</li>
    }
  </ul>
)

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      list: [],
      username: ''
    }
  }
  getUsername () {
    this.setState({
      username: event.target.value
    })
  }
  addUser () {
    const list = this.state.list
    list.push(this.state.username)
    this.setState({
      list
    })
  }
  deleteItem (index) { // index参数是子组件传递的
    console.log(index)
    const list = this.state.list
    list.splice(index, 1)
    this.setState({
      list
    })
  }
  render () {
    return (
      <div>
        <input type="text" value={ this.state.username } onChange = { this.getUsername.bind(this) }/>
        <button onClick={ this.addUser.bind(this) }>添加</button>
        <List list = { this.state.list } deleteItem = { this.deleteItem.bind(this) }/>
      </div>
    )
  }
}

export default App;


--------------------------------------------------------------------------文章来自吴大勋(大勋哥)链接


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM