05-React组件的组合使用

news2024/11/28 16:40:32

05-React组件的组合使用


1.TodoList案例

需求:TodoList组件化实现此功能

  1. 显示所有todo列表
  2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本

1).实现:

  1. 完成TodoList组件的静态页面以及拆解组件

  2. 动态初始化列表

    //App.jsx
    export default class App extends Component {
      // 初始化状态
      state = {
        todos: [
          { id: '001', name: '吃饭', done: true },
          { id: '002', name: '睡觉', done: true },
          { id: '003', name: '敲代码', done: false },
          { id: '004', name: '逛街', done: true },
        ]
      }
      render() {
        const { todos } = this.state
        return (
          <div className="todo-container">
            <div className="todo-wrap">
              <Header/>
              <List todos={todos}/>
              <Footer/>
            </div>
          </div>
        )
      }
    }
    
    //List.jsx
    export default class List extends Component {
      render() {
       // 接收来自App父组件传递的数据   
        const { todos } = this.props
        return (
          <ul className="todo-main">
            {todos.map(todo => {
              return <Item key={todo.id} {...todo}/>
            })}
          </ul>
        )
      }
    }
    
    //Item.jsx
    export default class Item extends Component {
      render() {
          // 接收来自List父组件传递的数据   
        const { id, name, done } = this.props
        return (
          <li>
            <label>
              <input type="checkbox" checked={done}/>
              <span>{name}</span>
            </label>
            <button>删除</button>
          </li>
        )
      }
    }
    

  3. 完成添加“任务”的功能(父子组件之间传值)

    在父组件中创建一个添加数据的方法addTodo,然后将方法传递给子组件,当子组件向该方法传递了参数后,父组件便可以接收到子组件传递的参数

    //App.jsx
    export default class App extends Component {
      // 初始化状态
      state = {
        todos: [
          { id: '001', name: '吃饭', done: true },
          { id: '002', name: '睡觉', done: true },
          { id: '003', name: '敲代码', done: false },
          { id: '004', name: '逛街', done: true },
        ]
      }
     //addTodo用于添加一个todo,接收的参数是todo对象
      addTodo = (todoObj) => {
        console.log('APP:' + data);
        // 获取原数组
        const { todos } = this.state
        // 追加一个新数组
        const newTodos = [todoObj, ...todos]
        // 更新状态
        this.setState({ todos: newTodos })
      }   
      render() {
        const { todos } = this.state
        return (
          <div className="todo-container">
            <div className="todo-wrap">
              <Header addTodo={this.addTodo}/>
              <List todos={todos}/>
              <Footer/>
            </div>
          </div>
        )
      }
    }
    

    子组件Header创建一个点击回车键回调的方法handlerKeyUp,在该方法中向从父组件接收到的方法addTodo中传递参数,将新添加的数据反馈给父组件

    //Header.jsx
    export default class Header extends Component {
      // 键盘事件的回调
      handlerKeyUp = (event) => {
        // keyCode 触发事件的按键码
        // 解构赋值keyCode, target
        const { keyCode, target } = event
        // 判断是否是回车按键
        if (keyCode !== 13) return
        // 添加的todo名字不能为空
        if (target.value.trim() == '') {
          alert('输入不能为空')
          return
        }
        // 准备一个新的todo对象
        const todoObj = {
         // 使用nanoid库创建一个不重复的ID   
          id: nanoid(),
          name: target.value,
          done: false
        }
        this.props.addTodo(todoObj);
        // 清空输入
        target.value='';
      }
      render() {
        return (
          <div className="todo-header">
            <input onKeyUp={this.handlerKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认" />
          </div>
        )
      }
    }
    

  4. 完成删除"任务"的功能(祖孙组件之间的传值)

    在祖组件中创建一个删除数据的方法deleteTodo,然后将方法传递给子组件,再由子组件传递给孙组件,当孙组件向该方法传递了参数后,祖组件便可以接收到子组件传递的参数

    //App.jsx
    export default class App extends Component {
      // 初始化状态
      state = {
        todos: [
          { id: '001', name: '吃饭', done: true },
          { id: '002', name: '睡觉', done: true },
          { id: '003', name: '敲代码', done: false },
          { id: '004', name: '逛街', done: true },
        ]
      }
      // deleteTodo用于删除一个todo对象
      deleteTodo = (id) => {
        const { todos } = this.state
        // 删除指定id的todo对象
        const newTodos = todos.filter(todoObj => {
          return todoObj.id !== id
        })
        // 更新状态
        this.setState({ todos: newTodos })
      }
      render() {
        const { todos } = this.state
        return (
          <div className="todo-container">
            <div className="todo-wrap">
              <Header addTodo={this.addTodo}/>
              <List todos={todos} deleteTodo={this.deleteTodo}/>
              <Footer/>
            </div>
          </div>
        )
      }
    }
    

    List组件接收从父组件传递来的删除数据的方法deleteTodo,再将deleteTodo方法传递给自己的子组件Item

    //List.jsx
    export default class List extends Component {
      render() {
        const { todos, updateTodo} = this.props
        return (
          <ul className="todo-main">
            {todos.map(todo => {
              return <Item key={todo.id} {...todo} deleteTodo={deleteTodo} />
            })}
          </ul>
        )
      }
    }
    

    Item组件创建一个删除“任务”的回调方法handleDelete,然后在该方法中调用deleteTodof方法,将需要删除的数据id传递给祖组件App

    //Item.jsx
    export default class Item extends Component {
      // 删除一个todo的回调
      handleDelete = (id) => {
        // console.log('通知App删除'+id);
        if (window.confirm('确定删除吗?')) {
          this.props.deleteTodo(id)
        }
      }
      render() {
        const { id, name, done } = this.props
        const { mouse } = this.state
        return (
          <li style={{ backgroundColor: mouse ? '#ddd' : 'white' }} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)}>
            <label>
              <input type="checkbox" checked={done} onChange={this.handleChange(id)} />
              <span>{name}</span>
            </label>
            <button onClick={() => this.handleDelete(id)} className="btn btn-danger" style={{ display: mouse ? 'block' : 'none' }}>删除</button>
          </li>
        )
      }
    }
    

  5. 为每个Item组件添加鼠标移入高亮且删除按钮显现、鼠标移出无高亮显示且删除按钮隐藏的效果

    //Item.jsx
    export default class Item extends Component {
        state = {
            mouse: false //标识鼠标移入、移出
        }
        //鼠标移入、移出的回调
        handleMouse = (flag) => {
            return () => {
                this.setState({ mouse: flag })
            }
        }
        // 删除一个todo的回调
        handleDelete = (id) => {
            if (window.confirm('确定删除吗?')) {
                this.props.deleteTodo(id)
            }
        }
        render() {
            const { id, name, done } = this.props
            const { mouse } = this.state
            return (
                <li style={{ backgroundColor: mouse ? '#ddd' : 'white' }} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)}>
                    <label>
                        <input type="checkbox" checked={done}/>
                        <span>{name}</span>
                    </label>
                    <button onClick={() => this.handleDelete(id)} className="btn btn-danger" style={{ display: mouse ? 'block' : 'none' }}>删除</button>
                </li>
            )
        }
    }
    

  6. 修改Item组件勾选框的状态和修改数据(祖孙组件之间的传值)

    在祖组件中创建一个更新数据的方法updateTodo,然后将方法传递给子组件,再由子组件传递给孙组件,当孙组件向该方法传递了参数后,祖组件便可以接收到子组件传递的参数

    //App.jsx
    export default class App extends Component {
      // 初始化状态
      state = {
        todos: [
          { id: '001', name: '吃饭', done: true },
          { id: '002', name: '睡觉', done: true },
          { id: '003', name: '敲代码', done: false },
          { id: '004', name: '逛街', done: true },
        ]
      }
      // updateTodo用于更新一个todo对象
      updateTodo = (id, done) => {
        // 获取状态中的todos
        const { todos } = this.state
        // 匹配处理数据
        const newTodos = todos.map(todoObj => {
          if (todoObj.id === id) return { ...todoObj, done }
          else return todoObj
        })
        this.setState({ todos: newTodos })
      }
      render() {
        const { todos } = this.state
        return (
          <div className="todo-container">
            <div className="todo-wrap">
              <Header addTodo={this.addTodo} />
              <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />
              <Footer/>
            </div>
          </div>
        )
      }
    }
    

    List组件接收从父组件传递来的删除数据的方法updateTodo,再将updateTodo方法传递给自己的子组件Item

    //List.jsx
    export default class List extends Component {
      render() {
        const { todos, updateTodo, deleteTodo } = this.props
        return (
          <ul className="todo-main">
            {todos.map(todo => {
              return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo} />
            })}
          </ul>
        )
      }
    }
    

    Item组件创建一个更新数据的回调方法handleDelete,然后在该方法中调用updateTodo方法,将需要更新的数据id传递给祖组件App

    export default class Item extends Component {
        state = {
            mouse: false //标识鼠标移入、移出
        }
        //鼠标移入、移出的回调
        handleMouse = (flag) => {
            return () => {
                this.setState({ mouse: flag })
                // console.log(flag);
            }
        }
        //勾选、取消勾选某一个todo的回调
        handleChange = (id) => {
            return (event) => {
                // console.log(id,event.target.checked);
                this.props.updateTodo(id, event.target.checked);
            }
        }
        // 删除一个todo的回调
        handleDelete = (id) => {
            // console.log('通知App删除'+id);
            if (window.confirm('确定删除吗?')) {
                this.props.deleteTodo(id)
            }
        }
        render() {
            const { id, name, done } = this.props
            const { mouse } = this.state
            return (
                <li style={{ backgroundColor: mouse ? '#ddd' : 'white' }} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)}>
                    <label>
                        <input type="checkbox" checked={done} onChange={this.handleChange(id)} />
                        <span>{name}</span>
                    </label>
                    <button onClick={() => this.handleDelete(id)} className="btn btn-danger" style={{ display: mouse ? 'block' : 'none' }}>删除</button>
                </li>
            )
        }
    }
    

  7. 完成底部组件全选修改数据的功能

    //APP.jsx
    export default class App extends Component {
        // 初始化状态
        state = {
            todos: [
                { id: '001', name: '吃饭', done: true },
                { id: '002', name: '睡觉', done: true },
                { id: '003', name: '敲代码', done: false },
                { id: '004', name: '逛街', done: true },
            ]
        }
        // checkAllTodo用于全选
        checkAllTodo=(done)=>{
            // 获取原来的todos
            const {todos}=this.state
            // 处理数据
            const newTodos=todos.map(todoObj=>{
                return {...todoObj,done}
            })
            // 更新数据
            this.setState({todos:newTodos})
        }
        render() {
            const { todos } = this.state
            return (
                <div className="todo-container">
                    <div className="todo-wrap">
                        <Header addTodo={this.addTodo} />
                        <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />
                        <Footer todos={todos} checkAllTodo={this.checkAllTodo}/>
                    </div>
                </div>
            )
        }
    }
    
    //Footer.jsx
    export default class Footer extends Component {
      handleChange=(event)=>{
        this.props.checkAllTodo(event.target.checked)
      }
      render() {
        const {todos}=this.props
        // 已完成的个数
        const doneCount=todos.reduce((pre,todo)=>pre+(todo.done?1:0),0)
        // 总数
        const total=todos.length
        return (
          <div className="todo-footer">
          <label>
            <input type="checkbox" onChange={this.handleChange} checked={doneCount===total&&total!=0?true:false}/>
          </label>
          <span>
            <span>已完成{doneCount}</span> / 全部{total}
          </span>
          <button className="btn btn-danger">清除已完成任务</button>
        </div>
        )
      }
    }
    

  8. 完成“清楚已完成任务”按钮的功能

    export default class App extends Component {
      // 初始化状态
      state = {
        todos: [
          { id: '001', name: '吃饭', done: true },
          { id: '002', name: '睡觉', done: true },
          { id: '003', name: '敲代码', done: false },
          { id: '004', name: '逛街', done: true },
        ]
      }
      // clearAllDone用于清除所有已完成的
      clearAllDone=()=>{
        // 获取原来的todos
        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">
              <Header addTodo={this.addTodo} />
              <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />
              <Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/>
            </div>
          </div>
        )
      }
    }
    
    export default class Footer extends Component {
      handleClearAllDone=()=>{
        this.props.clearAllDone()
      }
      render() {
        const {todos}=this.props
        // 已完成的个数
        const doneCount=todos.reduce((pre,todo)=>pre+(todo.done?1:0),0)
        // 总数
        const total=todos.length
        return (
          <div className="todo-footer">
          <label>
            <input type="checkbox" onChange={this.handleChange} checked={doneCount===total&&total!=0?true:false}/>
          </label>
          <span>
            <span>已完成{doneCount}</span> / 全部{total}
          </span>
          <button onClick={this.handleClearAllDone} className="btn btn-danger">清除已完成任务</button>
        </div>
        )
      }
    }
    

2).总结:

todoList案例相关知识点:

  1. 拆分组件、实现静态组件,注意:className、style的写法
  2. 动态初始化列表,如何确定将数据放在哪个组件的state中?
    1. 某个组件使用:放在其自身的state中
    2. 某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
  3. 关于父子之间通信:
    1. 【父组件】给【子组件】传递数据:通过props传递
    2. 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
  4. 注意defaultChecked和checked的区别,类似的还有:defaultValue和value
  5. 状态在哪里,操作状态的方法就在哪里

2.GitHub搜索案例

效果:

1).实现:

a.使用axios发送请求
  1. 搭建组件静态效果

    //List.jsx
    export default class List extends Component {
        render() {
            return (
                <div className="row">
                    <div className="card">
                        <a href="" target="_blank">
                            <img src=""/>
                        </a>
                        <p className="card-text">{user.login}</p>
                    </div>
                </div>
            )
        }
    }
    
    //Search.jsx
    export default class Search extends Component {
      render() {
        return (
          <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索Github用户</h3>
            <div>
              <inputtype="text" placeholder="输入关键词点击搜索" />&nbsp;
              <button>搜索</button>
            </div>
          </section>
        )
      }
    }
    
  2. 配置脚手架代理(配置代理)

    const { createProxyMiddleware } = require('http-proxy-middleware');
    module.exports = function (app) {
        app.use(
            createProxyMiddleware('/api1', {    
                target: 'http://localhost:5000',   
                secure: false,  //
                changeOrigin: true,    
                pathRewrite: {
                    '^/api1': '',
                },  
            })
        )
    }
    
  3. 完成搜索框的搜索和发送请求的功能

    1. 收集Search组件输入框的内容

      export default class Search extends Component {
          Search = () => {
              // 连续解构赋值+重命名
              const { KeyWordElement: { value: keyWord } } = this;
              console.log(keyWord);
          }
          render() {
              return (
                  <section className="jumbotron">
                      <h3 className="jumbotron-heading">搜索Github用户</h3>
                      <div>   
                          <input ref={c => this.KeyWordElement = c} type="text" placeholder="输入关键词点击搜索" />&nbsp;
                          <button onClick={this.Search}>搜索</button>
                      </div>
                  </section>
              )
          }
      }
      

    2. 发送请求

      export default class Search extends Component {
          Search = () => {
              const { KeyWordElement: { value: keyWord } } = this;
              // 发送请求
              axios.get(`/api1/search/users2?q=${keyWord}`).then(
                  response => { 
                      console.log(response.data.items);
                  },
                  error => { 
                      console.log('失败了', error); 
                  }
              )
          }
          render() {
              return (
                  <section className="jumbotron">
                      <h3 className="jumbotron-heading">搜索Github用户</h3>
                      <div>
                          <input ref={c => this.KeyWordElement = c} type="text" placeholder="输入关键词点击搜索" />&nbsp;
                          <button onClick={this.Search}>搜索</button>
                      </div>
                  </section>
              )
          }
      }
      

  4. 动态初始化组件的状态

    //App.jsx
    export default class App extends Component {
        // 初始化状态
        state={
            users:[],//users初始值为数组
            isFirst:true,//是否为第一次打开页面
            isLoading:false,//标识是否处于加载中
            err:""// 存储请求相关的错误信息
        }
        render() {
            const {users}=this.state
            return (
                <div className="container">
                    <Search/> 
                    <List {...this.state}/>
                </div>
            )
        }
    }
    
    //List.jsx
    export default class List extends Component {
        render() {
            const { users,isFirst,isLoading,err } = this.props
            return (
                <div className="row">
                    {
                        //使用三元运算符动态显示组件的内容
                        isFirst?<h3>欢迎使用,输入关键字,随后点击搜索</h3>:
                        isLoading?<h3>Loading......</h3>:
                        err?<h3 style={{color:'red'}}>{err}</h3>:
                        users.map(user => {
                            return (
                                <div className="card" key={user.id}>
                                    <a href={user.html_url} target="_blank">
                                        <img src={user.avatar_url} style={{ width: '100px' }} />
                                    </a>
                                    <p className="card-text">{user.login}</p>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
    }
    
  5. 发起请求并将数据显示到组件上

    //App.jsx
    export default class App extends Component {
        // 初始化状态
        state={
            users:[],//users初始值为数组
            isFirst:true,//是否为第一次打开页面
            isLoading:false,//标识是否处于加载中
            err:""// 存储请求相关的错误信息
        }
        // 更新App的state
        updateAppState=(stateObj)=>{
            this.setState(stateObj)
        }
        render() {
            const {users}=this.state
            return (
                <div className="container">
                    <Search updateAppState={this.updateAppState}/> 
                    <List {...this.state}/>
                </div>
            )
        }
    }
    
    //Search.jsx
    export default class Search extends Component {
      Search = () => {
        // 连续解构赋值+重命名
        const { KeyWordElement: { value: keyWord } } = this;
        //  发送请求前通知App更新状态
        this.props.updateAppState({ isFirst:false,isLoading:true,})
        // 发送请求
        axios.get(`/api1/search/users2?q=${keyWord}`).then(
          response => { 
            //请求成功后通知App更新状态
            this.props.updateAppState({isLoading:false,users:response.data.items})
          },
          error => { 
            console.log('失败了', error); 
            // 请求成功后通知App更新状态
            this.props.updateAppState({isLoading:false,err:error.message})
          }
        )
      }
      render() {
        return (
          <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索Github用户</h3>
            <div>
              <input ref={c => this.KeyWordElement = c} type="text" placeholder="输入关键词点击搜索" />&nbsp;
              <button onClick={this.Search}>搜索</button>
            </div>
          </section>
        )
      }
    }
    
b.使用PubSub实现(兄弟组件之间传值)

消息订阅-发布机制

  1. 工具库: PubSubJS

  2. 下载:

    npm install pubsub-js --save
    
  3. 使用:

    1. import PubSub from 'pubsub-js' //引入
      
    2. PubSub.subscribe('delete', function(data){ }); //订阅
      
    3. PubSub.publish('delete', data) //发布消息
      
    4. PubSub.unsubscribe(this.xxx)
      
  4. 初始化各个组件

    //App.jsx
    export default class App extends Component {
        render() {
            return (
                <div className="container">
                    <Search/>
                    <List/>
                </div>
            )
        }
    }
    
    //Search.jsx
    export default class Search extends Component {
        render() {
            return (
                <section className="jumbotron">
                    <h3 className="jumbotron-heading">搜索Github用户</h3>
                    <div>
                        <input type="text" placeholder="输入关键词点击搜索" />&nbsp;
                        <button>搜索</button>
                    </div>
                </section>
            )
        }
    }
    
    //List.jsx
    export default class List extends Component {
        render() {
            return (
                <div className="row">
                    <div className="card">
                        <a href="" target="_blank">
                            <img src=""/>
                        </a>
                        <p className="card-text">{user.login}</p>
                    </div>
                </div>
            )
        }
    }
    
  5. 初始化组件的状态

    //List.jsx
    export default class List extends Component {
      // 初始化状态
      state = {
        users: [],
        isFirst: true,
        isLoading: false,
        err: ""
      }
      render() {
        const { users, isFirst, isLoading, err } = this.state
        return (
          <div className="row">
            {
              isFirst ? <h3>欢迎使用,输入关键字,随后点击搜索</h3> :
                isLoading ? <h3>Loading......</h3> :
                  err ? <h3 style={{ color: 'red' }}>{err}</h3> :
                    users.map(user => {
                      return (
                        <div className="card" key={user.id}>
                          <a href={user.html_url} target="_blank">
                            <img src={user.avatar_url} style={{ width: '100px' }} />
                          </a>
                          <p className="card-text">{user.login}</p>
                        </div>
                      )
                    })
            }
          </div>
        )
      }
    }
    
  6. 发布消息与发送请求数据

    //Search.jsx
    export default class Search extends Component {
      Search = () => {
        const { KeyWordElement: { value: keyWord } } = this;
        PubSub.publish('Spongebob',{isFirst:false,isLoading:true})
        axios.get(`/api1/search/users2?q=${keyWord}`).then(
          response => { 
            PubSub.publish('Spongebob',{isLoading:false,users:response.data.items})
          },
          error => { 
            PubSub.publish('Spongebob',{isLoading:false,err:error.message})
          }
        )
      }
      render() {
        return (
          <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索Github用户</h3>
            <div>
              <input ref={c => this.KeyWordElement = c} type="text" placeholder="输入关键词点击搜索" />&nbsp;
              <button onClick={this.Search}>搜索</button>
            </div>
          </section>
        )
      }
    }
    
  7. 订阅消息与动态更新数据

    export default class List extends Component {
      // 初始化状态
      state = {
        users: [],
        isFirst: true,
        isLoading: false,
        err: ""
      }
      componentDidMount() {
        this.token = PubSub.subscribe('Spongebob', (_, stateObj) => {
          this.setState(stateObj)
        })
      }
      componentWillUnmount() {
       // 组件销毁后取消订阅消息   
        PubSub.unsubscribe(this.token)
      }
      render() {
        const { users, isFirst, isLoading, err } = this.state
        return (
          <div className="row">
            {
              isFirst ? <h3>欢迎使用,输入关键字,随后点击搜索</h3> :
                isLoading ? <h3>Loading......</h3> :
                  err ? <h3 style={{ color: 'red' }}>{err}</h3> :
                    users.map(user => {
                      return (
                        <div className="card" key={user.id}>
                          <a href={user.html_url} target="_blank">
                            <img src={user.avatar_url} style={{ width: '100px' }} />
                          </a>
                          <p className="card-text">{user.login}</p>
                        </div>
                      )
                    })
            }
          </div>
        )
      }
    }
    
c.使用fetch发送请求

Fetch

特点:

  1. fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
  2. 老版本浏览器可能不支持
export default class Search extends Component {
    Search = async() => {
        const { KeyWordElement: { value: keyWord } } = this;
        PubSub.publish('Spongebob',{isFirst:false,isLoading:true})
        // 发送请求
        try {
            const response=await fetch(`/api1/search/users2?q=${keyWord}`);
            PubSub.publish('Spongebob',{isFirst:false,isLoading:true})
            const result=await response.json();
            // PubSub.publish('Spongebob',{isLoading:false,users:result})
            console.log(result);
        } catch (error) {
            console.log('请求出错',error);
        }
    }
    render() {
        return (
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索Github用户</h3>
                <div>
                    <input ref={c => this.KeyWordElement = c} type="text" placeholder="输入关键词点击搜索" />&nbsp;
                    <button onClick={this.Search}>搜索</button>
                </div>
            </section>
        )
    }
}

2).总结:

github搜索案例相关知识点:

  1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。

  2. ES6小知识点:解构赋值+重命名

    let obj {a:{b:1}}
    //传统解构赋值
    const{a}=obj;
    //连续解构赋值
    const{a:(b}}=obj;
    //连续解构赋值+重命名
    const{a:{b:value}}=obj;
    
  3. 消息订阅与发布机制

    1. 先订阅,再发布(理解:有一种隔空对话的感觉)
    2. 适用于任意组件间通信
    3. 要在组件的componentwillUnmount中取消订阅
  4. fetch发送请求(关注分离的设计思想)

    try{
        const response=await fetch(/api1/search/users2?q=${keyWord))
        const data await response.json()
        console.log(data);
    }catch (error){
        console.1og('请求出错',error);
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1096401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[初始java]——java为什么这么火,java如何实现跨平台、什么是JDK/JRE/JVM

java的名言&#xff1a; ”一次编译、到处运行“ 一、编译语言与解释语言 编译&#xff1a; 是将整份源代码转换成机器码再进行下面的操作&#xff0c;最终形成可执行文件 解释&#xff1a; 是将源代码逐行转换成机器码并直接执行的过程&#xff0c;不需要生成目标文件 jav…

10.14~10.15verilog操作流程与Block Design

后面的那个是延时精度 verilog文件结构 文件名称与写的模板没有关系&#xff0c;这个文件名为P1,但模板名为andgate 但是如果是仿真文件&#xff0c;就需要开头的模板名和仿真文件名相同 .v是源文件&#xff0c;设计文件 .v在设计与sim里都有&#xff0c;静态共享&#xff0…

卡顿分析与布局优化

卡顿分析与布局优化 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能。Android系统每隔大概16.6ms发出VSYNC信 号&#xff0c;触发对UI进行渲染&#xff0c;如果每次渲染都成功&#xff0c;这样就能够达到流畅的画面所需要的60fps&#xff0c;为了能够实现60fp…

CISA 彻底改变了恶意软件信息共享:网络安全的突破

在现代网络安全中&#xff0c;战术技术和程序&#xff08;TTP&#xff09;的共享对于防范网络事件至关重要。 因此&#xff0c;了解攻击向量和攻击类型之间的关联如今是让其他公司从其他公司遭受的 IT 事件中受益&#xff08;吸取经验教训&#xff09;的重要一步。 美国主要网…

多模态大模型升级:LLaVA→LLaVA-1.5,MiniGPT4→MiniGPT5

Overview LLaVA-1.5总览摘要1.引言2.背景3.LLaVA的改进4.讨论附录 LLaVA-1.5 总览 题目: Improved Baselines with Visual Instruction Tuning 机构&#xff1a;威斯康星大学麦迪逊分校&#xff0c;微软 论文: https://arxiv.org/pdf/2310.03744.pdf 代码: https://llava-vl.…

无法解析符号 ‘SpringBootApplication’

刚打开一个项目出现"SpringBootApplication"无法解析&#xff1a; 通过以下步骤&#xff0c;修改maven路径即可&#xff1a; 文件---->设置&#xff08;File--->Settings&#xff09; 构建、执行、部署--->构建工具--->Maven--->Maven主路经&#xf…

07-ConfigurationClassPostProces的解析

文章目录 如何解析Component,Service,Configurationd,Bean,Import等注解1. 源码描述2. 类继承结构图3. 解析流程4. 具体的注解解析 如何解析Component,Service,Configurationd,Bean,Import等注解 1. 源码描述 BeanFactoryPostProcessor used for bootstrapping processing of…

论文笔记[156]PARAFAC. tutorial and applications

原文下载&#xff1a;https://www.sciencedirect.com/science/article/abs/pii/S0169743997000324 摘要 本文介绍了PARAFAC的多维分解方法及其在化学计量学中的应用。PARAFAC是PCA对高阶数组的推广&#xff0c;但该方法的一些特性与普通的二维情况截然不同。例如&#xff0c;…

O2O优惠券预测

O2O优惠券预测 赛题理解赛题类型解题思路 数据探索理论知识数据可视化分布 特征工程赛题特征工程思路 模型训练与验证 赛题理解 赛题类型 本赛题要求提交的结果是预测15 天内用券的概率&#xff0c;这是一个连续值&#xff0c;但是因为用券只有用与不用两种情况&#xff0c;而…

IDEA中为Maven配置使用vpn工具连接的网络

IDEA中为Maven配置使用vpn工具连接的网络 在电脑上使用VPN工具连接上特定网络后&#xff0c;发现idea中使用maven工具还是无法访问相关的网络&#xff0c;这时需要在idea中进行相关配置&#xff0c;开启ipv4代理 -Djava.net.preferIPv4Stacktrue maven配置 这时&#xff0c;…

JavaScript基础知识13——运算符:一元运算符,二元运算符

哈喽&#xff0c;大家好&#xff0c;我是雷工。 JavaScript的运算符可以根据所需表达式的个数&#xff0c;分为一元运算符、二元运算符、三元运算符。 一、一元运算符 1、一元运算符&#xff1a;只需要一个表达式就可以运算的运算符。 示例&#xff1a;正负号 一元运算符有两…

Golang 协程 与 Java 线程池的联系

Golang 协程 与 Java 线程池的联系 引言Java 线程池缺陷Golang 协程实现思路0.x 版本1.0 版本1.1 版本Goroutine 抢占式执行基于信号的抢占式调度 队列轮转系统调用工作量窃取GOMAXPROCS设置对性能的影响 小结 引言 如何理解Golang的协程&#xff0c;我觉得可以用一句话概括: …

【大数据】Hive SQL语言(学习笔记)

一、DDL数据定义语言 1、建库 1&#xff09;数据库结构 默认的数据库叫做default&#xff0c;存储于HDFS的&#xff1a;/user/hive/warehouse 用户自己创建的数据库存储位置&#xff1a;/user/hive/warehouse/database_name.db 2&#xff09;创建数据库 create (database|…

c#设计模式-行为型模式 之 备忘录模式

&#x1f680;简介 备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为型设计模式&#xff0c;它保存一个对象的某个状态&#xff0c;以便在适当的时候恢复对象。所谓备忘录模式就是在不破坏封装的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象…

Android 14 正式发布,已经在 AOSP 中上线

本心、输入输出、结果 文章目录 Android 14 正式发布,已经在 AOSP 中上线前言总结主要更新内容机型支持优化性能的数据体现字体放大、多媒体支持加强Android 14 增加了对 10 位高动态范围 (HDR) 图像的支持提供了新的图形和尺寸管理用户体验 与隐私安全弘扬爱国精神Android 14…

Vue3 + Nodejs 实战 ,文件上传项目--实现拖拽上传

目录 1.拖拽上传的剖析 input的file默认拖动 让其他的盒子成为拖拽对象 2.处理文件的上传 处理数据 上传文件的函数 兼顾点击事件 渲染已处理过的文件 测试效果 3.总结 博客主页&#xff1a;専心_前端,javascript,mysql-CSDN博客 系列专栏&#xff1a;vue3nodejs 实战-…

【JVM】JVM的内存区域划分

JVM的内存区域划分 堆Java虚拟机栈程序计数器方法区运行时常量池 堆 程序中创建的所有对象都保存在堆中 Java虚拟机栈 Java虚拟机栈的生命周期和线程相同,描述的是Java方法执行的内存模型,每个方法在执行的时候都会同时创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法…

C语言 —— 结构体

生活中有许多复杂对象是无法用基本数据类型来描述的, 于是为了描述复杂对象, C语言就会使用到结构体. 1. 结构体的声明与定义 1.1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明与定义 struct tag {…

vue3后台管理框架之基础配置

配置vite.config.js import { defineConfig } from viteimport vue from @vitejs/plugin-vueexport default defineConfig(({ command, mode }) => {//const env = loadEnv(mode, process.cwd(), ) //获取环境变量return {// 打包devbase: ./,// 开发环境server: {port: 50…

Spring framework Day11:策略模式中注入所有实现类

前言 什么是策略模式&#xff1f; 策略模式&#xff08;Strategy Pattern&#xff09;是一种面向对象设计模式&#xff0c;它定义了算法族&#xff08;一组相似的算法&#xff09;&#xff0c;并且将每个算法都封装起来&#xff0c;使得它们可以互相替换。策略模式让算法的变…