React复习(5)

(1)组件数据源

props是组件对外接口,上层组件通过props把数据和方法传给下层组件

state是对内接口,是私有的,用来组件维护自己的私有状态

视图层:当props和state变化时,react会重新渲染

(2)组件间传值类型检查

类型检查有两种写法,一种是写在组件内,一种写在外面,功能相同

class Student extends React.Component {
  //添加静态属性,react自带的
  static defaultProps = {
    single: '保密'
  }
  static propTypes = {
    name: PropTypes.string.isRequired,
    age: PropTypes.number.isRequired
  }
  constructor(props) {
    super(props)
    console.log(props);
  }
  render() {
    // console.log(this.props);
    let { name, age, single } = this.props
    return (
      <ul>
        <li>name:{name}</li>
        <li>age:{age}</li>
        <li>single:{single}</li>
      </ul>
    )
  }
}
//defaultProps发生在 constructor构造函数执行之前
//defaultProps是react自带的,不需要引入库
//该静态属性可以添加默认值
Student.defaultProps = {
  single: '保密'
}
//传值类型检查
Student.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.isRequired
}
const student = {
  name: 'jack',
  age: 18
}
ReactDOM.render(<Student {...student} />, document.getElementById('app'))

propType是检查传值类型的工具,规范传值的数据类型和是否必要,如果要用的话要先引用

<script src="./js/prop-types.js"></script>

React里有自带的属性defaultProps,它是一个对象,可以给参数传默认值

(3)task案例

单组件案例

class App extends React.Component {
  //数据
  state = {
    tasks: [],
    task: '',
    date: new Date().toLocaleDateString()
  }
  //同步输入框
  handelChange = (e) => {
    this.setState({
      task: e.target.value
      //利用回调函数解决setState异步的问题
    }, () => {
      // console.log("task", this.state.task)
    })
  }
  //添加任务
  handleAddTask = (e) => {
    const { task, tasks } = this.state
    //使任务不为空
    if (!task) {
      return
    }
    //unshift返回值是新数组的长度 不是新的数组
    tasks.unshift(task)
    this.setState({
      tasks: tasks,
      task: ' ',
      //保证时间是最新同步的
      date: new Date().toLocaleDateString()
    }, () => { })
  }
  // 删除任务
  handleDelete(index) {
    const { tasks } = this.state
    //1指删除的个数
    tasks.splice(index, 1)
    this.setState({
      tasks:tasks
    }, () => { })
  }
  render() {
    return (
      <div>
        <h1>Today Tasks : {this.state.tasks.length}</h1>
        <div>
          <input type="text" value={this.state.task} onChange={this.handelChange} />
          <button onClick={this.handleAddTask}>添加</button>
        </div>
        <div>
          <ul>
            {
              //可以传两个参数,一个数组项,一个序号
              this.state.tasks.map((task, index) => {
                return (
                  <li key={index}>
                    <span>{index + 1}--{task}--{this.state.date}</span>
                    {/*通过bind改变this指向,传入index值给函数使用*/}
                    <button onClick={this.handleDelete.bind(this, index)}>delete</button>
                  </li>
                )
              })
            }
          </ul>
        </div>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

多组件构建思路

结构
App根组件
    添加组件,列表组件,列表项组件
数据位置
    某个组件自己需要,直接定义到自己组件里;若共同需要,则定义到父组件里
行为
    数据在哪,修改数据的行为就在哪,其他组件通过调用来触发方法
组件间通信
    子组件修改父组件利用props
组件类型
    若有自己的数据则定义成类组件

多组件案例

class App extends React.Component {
  state = {
    tasks: []
  }
  //添加     task为AddTask组件传来参数
  addTask = (task) => {
    const { tasks } = this.state
    //使任务不为空
    if (!task) {
      return
    }
    //使每个任务都有独立的时间
    let taskNew = { task: task, date: new Date().toLocaleTimeString() }
    // unshift 是新数组的长度 不是新的数组
    tasks.unshift(taskNew)
    this.setState({
      tasks: tasks,
    })
  }
  //删除
  deleteTask = (index) => {
    const { tasks } = this.state
    tasks.splice(index, 1)
    this.setState({
      tasks
    }, () => { })
  }
  render() {
    return (
      <div>
        <h1>Today Tasks : {this.state.tasks.length}</h1>
        <AddTask addTask={this.addTask} />
        <TaskList tasks={this.state.tasks} deleteTask={this.deleteTask} />
      </div>
    )
  }
}
//添加任务组件
class AddTask extends React.Component {
  state = { task: '' }
  //输入框同步
  handelChange = (e) => {
    this.setState({
      task: e.target.value
    }, () => { })
  }
  handleAddTask = () => {
    const { task } = this.state
    this.props.addTask(task)
    this.setState({
      task: ''
    })
  }
  render() {
    return (
      <div>
        <input type="text" onChange value={this.state.task} onChange={this.handelChange} />
        <button onClick={this.handleAddTask}>添加</button>
      </div>
    )
  }
}
//任务列表组件
function TaskList(props) {
  return (
    <div>
      <ul>
        {
          //              item是一个对象
          props.tasks.map((item, index) => {
            return <TaskItem key={index} index={index} task={item.task} date={item.date} deleteTask={props.deleteTask} />
          })
        }
      </ul>
    </div>
  )
}
//单项任务组件
function TaskItem(props) {
  function handleDelete() {
    props.deleteTask(props.index)
  }
  return (
    <li>
      <span>{props.index + 1}--{props.task}--{props.date}</span>
      <button onClick={handleDelete}>delete</button>
    </li>
  )
}
ReactDOM.render(<App />, document.getElementById('app'))

(4)context (发布订阅模式)

有时我们需要多层嵌套才能完成需求,如果只用props传值太过于繁琐,context就能很好的解决这个问题,它不仅能传值,还能传方法

const themes = {
  light: {
    color: '#9e9e9e',
    background: '#eeeeee'
  },
  dark: {
    color: '#e0e0e0',
    background: '#222222'
  }
}
//创建发布订阅组件
const { Provider, Consumer } = React.createContext({ theme: themes.light })
class App extends React.Component {
  state = { theme: themes.light }
  handleClick = () => {
    const theme = this.state.theme === themes.light ? themes.dark : themes.light
    this.setState({
      theme: theme
    })
  }
  render() {
    // 1:Provider组件通过value提供给包裹子组件发布值
    // 允许消费者订阅context变化,value变化时Consumer会重新渲染
    // 当一个组件有多个Provider提供的上下文,则最后一个会覆盖其他的
    // 被Provider包裹组件的的所有所包裹的组件全可以用到Provider发布的值
    // 本质上:this.context = Provider所提供的value值
    return (
      <div>
        {/*
          默认值是一个对象,为了组件能成功解析被Provider包裹和不被包裹两种情况,
          给value也设置成对象,而且key值要与默认值里的key相同
        */}
        {/*第一个是对象,第二个是方法*/}
        <Provider value={{ theme: this.state.theme, changeTheme: this.handleClick }}>
          <h1 style={{ color: this.state.theme.color, background: this.state.theme.background }}>content</h1>
          <ThemeButton />
          <Child1 />
        </Provider>
        <Child1 />
      </div>
    )
  }
}
function ThemeButton() {
  return (
    <Consumer>
      {
        (obj) => {
          console.log('button' + obj);
          //注意对象和方法的接收方式不同
          return <button style={{ ...obj.theme }} onClick={obj.changeTheme}>改变主题</button>
        }
      }
    </Consumer>
  )
}
function Child1(props) {
  //2:Consumer里有一个表达式,表达式里有一个函数,函数里有一个返回值
  //这个返回值才是最终渲染出来的组件
  return (
    <div>
      <Consumer>
        {
          (obj) => {
            console.log(obj)
            //obj就是传递过来的参数对象,自动解析
            return <h1 style={{ ...obj.theme }}>Child1</h1>
          }
        }
      </Consumer>
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('app'))

多个provider时解决方案

const MoneyContext = React.createContext(0)
const HouseContext = React.createContext('')
class Father extends React.Component {
  state = { money: 50, house: 'apartment' }
  render() {
    return (
      <MoneyContext.Provider value={this.state.money}>
        <HouseContext.Provider value={this.state.house} >
          <p>father money: {this.state.money}</p>
          <p>father house: {this.state.house}</p>
          <Son />
        </HouseContext.Provider>
      </MoneyContext.Provider>
    )
  }
}
class Son extends React.Component {
  //contextType和Consumer配合使用
  //添加一个静态属性,使组件能获取第一个Provider所提供的值
  static contextType = MoneyContext
  render() {
    console.log(this.context);
    const money = this.context
    const house = this.context
    return (
      <div>
        <p>son :获取father money : {money}</p>
        <HouseContext.Consumer>
          {
            (house) => {
              console.log(house);
              return <p>{house}</p>
            }
          }
        </HouseContext.Consumer>
      </div>
    )
  }
}
ReactDOM.render(<Father />, document.getElementById("app"));

context-task案例

//传递默认值
const { Provider, Consumer } = React.createContext({
  task: [],
  addTask: () => { },
  deleteTask: () => { }
})
//根组件
class App extends React.Component {
  //直接写的话this上只有简单数据没有方法
  // state = {
  //     tasks:[],
  //     addTask:this.addTask,
  //     deleteTask:this.deleteTask
  // }
  constructor() {
    super()
    //必须经过挂载才能把方法一起传进去
    this.state = {
      tasks: [],
      addTask: this.addTask,
      deleteTask: this.deleteTask
    }
  }
  //添加task为AddTask组件传来参数
  addTask = (task) => {
    const { tasks } = this.state
    if (!task) {
      return
    }
    let taskNew = { task: task, date: new Date().toLocaleTimeString() }
    tasks.unshift(taskNew)
    this.setState({
      tasks: tasks,
    })
  }
  //删除
  deleteTask = (index) => {
    const { tasks } = this.state
    tasks.splice(index, 1)
    this.setState({
      tasks
    }, () => { })
  }
  
  render() {
    return (
      <div>
        <h1>Today Tasks : {this.state.tasks.length}</h1>
        <Provider value={this.state}>
          <AddTask />
          <TaskList />
        </Provider>
      </div>
    )
  }
}
//添加任务组件
class AddTask extends React.Component {
  state = { task: '' }
  //输入框同步
  handelChange = (e) => {
    this.setState({
      task: e.target.value
    }, () => { })
  }
  handleAddTask(addTask) {
    const { task } = this.state
    addTask(task)
    //清楚input
    this.setState({
      task: ''
    })
  }
  render() {
    return (
      <div>
        <input type="text" onChange value={this.state.task} onChange={this.handelChange} />
        <Consumer>
          {
            //解构赋值,吧addTask拿出来
            ({ addTask }) => {
              //bind改变this指向
              return <button onClick={this.handleAddTask.bind(this, addTask)}>添加</button>
            }
          }
        </Consumer>
      </div>
    )
  }
}
//任务列表组件
function TaskList(props) {
  return (
    <div>
      <Consumer>
        {
          ({ tasks }) => {
            return <ul>
              {
                tasks.map((item, index) => {
                  return <TaskItem key={index} index={index} task={item.task} date={item.date} />
                })
              }
            </ul>
          }
        }
      </Consumer>
    </div>
  )
}
//单项任务组件
function TaskItem(props) {
  // function handleDelete() {
  //   props.deleteTask(props.index)
  // }
  return (
    <li>
      <span>{props.index + 1}--{props.task}--{props.date}</span>
      <Consumer>
        {
          ({ deleteTask }) => {
            return <button onClick={() => {deleteTask(props.index)}}>删除</button>
          }
        }
      </Consumer>
    </li>
  )
}
ReactDOM.render(<App />, document.getElementById('app'))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无非是我

不必打赏,欢迎指正

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值