react-父子之间数据通信

一.实现效果

二.案例总结 

        1.动态初始化列表,如何确认数据存放在那个组件的state中

                1)某个组件使用:放在自身的state中

                2)某些组件使用:放在他们共同的父组件state中(状态提升)

        2.关于父子之间数据通信:

                1)父组件 给 子组件 传递数据:通过props传递

                2)子组件 给 父组件 传递数据:通过props传递,要求父亲提前给儿子传递一个函数过去。

        3.注意 defaultChecked 和 checked 的区别:defaultChecked只在初始化时调用一次;使用checked需要配合onChange使用。类似的还有defaultValue和value。

        4.状态在哪里,操作状态的方法就在哪里。

        5.此实例中用到uuid(文件大,正式项目中用)、nanoid(练习用这个就好)和prop-types第三方插件,react没有安装需要自己安装。

npm add prop-types //安装prop-types

npm i nanoid     //安装nanoid    

三、具体代码实现

1.构思拆分组件 

2.创建目录

3.编写静态样式

App.jsx:

import React from 'react';
import Headers from './components/Header'
import Item from './components/Item'
import Footer from './components/Footer'
import './App.css';

class App extends React.Component{
  
  render(){
    
    return (
      <div className="todo-container">
        <div className="todo-wrap">
          <Headers />
          <Item />
          <Footer />
        </div>
      </div>
    );
  }
}

export default App;

App.css

/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

Header的jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'
import { nanoid } from 'nanoid'

export default class Header extends Component {
  
  render() {
    return (
        <div className="todo-header">
            <input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
        </div>
    )
  }
}

Header的css

/*header*/
.todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 4px 7px;
  }
  
  .todo-header input:focus {
    outline: none;
    border-color: rgba(82, 168, 236, 0.8);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  }

Item的jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import List from '../List'
import './index.css'

export default class Item extends Component {
  
  render() {

    return (
      <div>
        <ul className="todo-main">
          <List />
        </ul>
      </div>
    )
  }
}

Item的css

.todo-main {
    margin-left: 0px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding: 0px;
  }
  
  .todo-empty {
    height: 40px;
    line-height: 40px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding-left: 5px;
    margin-top: 10px;
  }

List的jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'

export default class List extends Component {
  
  render() {
    return (
        <li>
            <label>
            <input type="checkbox"/>
            <span>{name}</span>
            </label>
            <button className="btn btn-danger" style={{display:'none'}}>删除</button>
        </li>
    )
  }
}

List的css

li {
    list-style: none;
    height: 36px;
    line-height: 36px;
    padding: 0 5px;
    border-bottom: 1px solid #ddd;
  }
  
  li label {
    float: left;
    cursor: pointer;
  }
  
  li label li input {
    vertical-align: middle;
    margin-right: 6px;
    position: relative;
    top: -1px;
  }
  
  li button {
    float: right;
    display: none;
    margin-top: 3px;
  }
  
  li:before {
    content: initial;
  }
  
  li:last-child {
    border-bottom: none;
  }

Footer.jsx

import React, { Component } from 'react'
import './index.css'

export default class Footer extends Component {
  
  render() {
    return (
        <div className="todo-footer">
            <label>
            <input type="checkbox" />
            </label>
            <span>
            <span>已完成0</span> / 全部3
            </span>
            <button className="btn btn-danger">清除已完成任务</button>
        </div>
    )
  }
}

Footer.css

/*footer*/
.todo-footer {
    height: 40px;
    line-height: 40px;
    padding-left: 6px;
    margin-top: 5px;
  }
  
  .todo-footer label {
    display: inline-block;
    margin-right: 20px;
    cursor: pointer;
  }
  
  .todo-footer label input {
    position: relative;
    top: -1px;
    vertical-align: middle;
    margin-right: 5px;
  }
  
  .todo-footer button {
    float: right;
    margin-top: 5px;
  }
  

4.编写动态数据

App的jsx

import React from 'react';
import Headers from './components/Header'
import Item from './components/Item'
import Footer from './components/Footer'
import './App.css';

class App extends React.Component{
  // 初始化数据
  state = {todos:[
    {id:'001',name:'跳舞',done:false},
    {id:'002',name:'唱歌',done:false},
    {id:'003',name:'逛街',done:true},
    {id:'004',name:'吃饭',done:true},
  ]}
  // 添加数据
  addTodo = (todoObj)=>{
    // 获取原来的todos数据
    const {todos} = this.state
    // 追加一个todo
    const newTodos = [todoObj,...todos]
    this.setState({todos:newTodos})
  }
  // 修改数据(复选框选中需要更改done值)
  changeTodo = (id,done)=>{
    const {todos} = this.state
    const newTodos = todos.map((todoObj)=>{
      if(todoObj.id === id) return {...todoObj,done}  
      else return {...todoObj}
    })
    this.setState({todos:newTodos})
  }
  // 删除数据
  deleteTodo = (id)=>{
    const {todos} = this.state
    // 把数据过滤,如果原始数据id与返回来的id不相等就返回,相等不做任何操作。最后把过滤后的数据进行更新
    const newTodos = todos.filter((todoObj)=>{
      return todoObj.id !== id
    })
    this.setState({todos:newTodos})
  }
  // 选中全部数据
  checkedAllInfo = (done)=>{
    const {todos} = this.state
    const newTodos = todos.map(todoObj =>{
      return {...todoObj,done}
    })
    this.setState({todos:newTodos})
  }
  // 清除选中数据
  clearInfoSuccess=()=>{
    const {todos} = this.state
    const newTodos = todos.filter(todoObj => {
      return !todoObj.done
    })
    this.setState({todos:newTodos})
  }
  render(){
    const {todos} = this.state
    return (
      <div className="todo-container">
        <div className="todo-wrap">
          <Headers addTodo={this.addTodo}/>
          <Item todos={todos} changeTodo={this.changeTodo} deleteTodo={this.deleteTodo} />
          <Footer todos={todos} checkedAllInfo={this.checkedAllInfo} clearInfoSuccess={this.clearInfoSuccess}/>
        </div>
      </div>
    );
  }
}

export default App;

Header的jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'
import { nanoid } from 'nanoid'

export default class Header extends Component {
  // 对接收的props进行:类型、必要性的限制
  static propTypes = {
    addTodo:PropTypes.func.isRequired
  }
  // 键盘抬起事件(onKeyUp)  判断回车键抬起后做的操作
  handleKeyUp = (event)=>{
    const {target,keyCode} = event
    // 判断是否是回车按键
    if(keyCode !== 13) return 
    // 返回值接的是一组对象,id的序号是唯一的所以使用nanoid
    const todoObj = {id:nanoid(),name:target.value,done:false}
    this.props.addTodo(todoObj)
    // 数据添加成功后,把输入框数据清空
    target.value = ''
  }
  render() {
    return (
        <div className="todo-header">
            <input type="text"  onKeyUp={this.handleKeyUp} placeholder="请输入你的任务名称,按回车键确认"/>
        </div>
    )
  }
}

Item的jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import List from '../List'
import './index.css'

export default class Item extends Component {
  // 对接收的props进行:类型、必要性的限制
  static propTypes = {
    todos:PropTypes.array.isRequired,
    changeTodo:PropTypes.func.isRequired,
    deleteTodo:PropTypes.func.isRequired
  }
  // 这个文件只是做数据转储工作,给子组件传数据可以用{...todo}
  render() {
    const {todos,changeTodo,deleteTodo} = this.props
    return (
      <div>
        <ul className="todo-main">
          {/* 循环数据 */}
          {
            todos.map( todo => {
              return <List key={todo.id} {...todo} changeTodo={changeTodo} deleteTodo={deleteTodo} />
            })
          }
        </ul>
      </div>
    )
  }
}

List的jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'

export default class List extends Component {
  // 对接收的props进行:类型、必要性的限制
  static propTypes = {
    changeTodo:PropTypes.func.isRequired,

  }
  // 设置鼠标移入的初始值
  state = {mouse:false}
  // 鼠标移入移出事件:onMouseEnter()和onMouseLeave()
  // 当鼠标移入时,传true;当鼠标移出时,传false
  handleMouse = (flag)=>{
    return ()=>{
      this.setState({mouse:flag})
    }
  }
  // 修改选中状态
  changeChecked = (id)=>{
    return (event)=>{
      this.props.changeTodo(id,event.target.checked)
    }
  }
  // 删除数据
  delTodo = (event)=>{
    console.log("删除id",event)
    this.props.deleteTodo(event)
  }
  render() {
    const {id,name,done} = this.props
    const {mouse} = this.state
    return (
        <li style={{backgroundColor:mouse?'#ddd':'#fff'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
            <label>
            <input type="checkbox" checked={done} onChange={this.changeChecked(id)}/>
            <span>{name}</span>
            </label>
            <button className="btn btn-danger" onClick={()=>{this.delTodo(id)}} style={{display:mouse?'block':'none'}}>删除</button>
        </li>
    )
  }
}

Footer的jsx

import React, { Component } from 'react'
import './index.css'

export default class Footer extends Component {
  // 全选
  AllChecked = (event)=>{
    this.props.checkedAllInfo(event.target.checked)
  }
  // 清除完成状态数据
  clearTodos=()=>{
    this.props.clearInfoSuccess()
  }
  render() {
    const {todos} = this.props
    const total = todos.length
    const selectNum = todos.reduce((pre,todo)=>{
      return pre + (todo.done?1:0)
    },0)
    return (
        <div className="todo-footer">
            <label>
            <input type="checkbox" checked={selectNum == total&&total !== 0?true:false} onChange={this.AllChecked}/>
            </label>
            <span>
            <span>已完成{selectNum}</span> / 全部{total}
            </span>
            <button className="btn btn-danger" onClick={this.clearTodos}>清除已完成任务</button>
        </div>
    )
  }
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值