05-React组件的组合使用

05-React组件的组合使用


1.TodoList案例

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

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

1).实现:

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

  2. 动态初始化列表

    jsx 复制代码
    //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>
        )
      }
    }
    jsx 复制代码
    //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>
        )
      }
    }
    jsx 复制代码
    //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,然后将方法传递给子组件,当子组件向该方法传递了参数后,父组件便可以接收到子组件传递的参数

    jsx 复制代码
    //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中传递参数,将新添加的数据反馈给父组件

    jsx 复制代码
    //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,然后将方法传递给子组件,再由子组件传递给孙组件,当孙组件向该方法传递了参数后,祖组件便可以接收到子组件传递的参数

    jsx 复制代码
    //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

    jsx 复制代码
    //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

    jsx 复制代码
    //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组件添加鼠标移入高亮且删除按钮显现、鼠标移出无高亮显示且删除按钮隐藏的效果

    jsx 复制代码
    //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,然后将方法传递给子组件,再由子组件传递给孙组件,当孙组件向该方法传递了参数后,祖组件便可以接收到子组件传递的参数

    jsx 复制代码
    //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

    jsx 复制代码
    //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

    jsx 复制代码
    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. 完成底部组件全选修改数据的功能

    jsx 复制代码
    //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>
            )
        }
    }
    jsx 复制代码
    //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. 完成"清楚已完成任务"按钮的功能

    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 },
        ]
      }
      // 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>
        )
      }
    }
    jsx 复制代码
    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. 搭建组件静态效果

    jsx 复制代码
    //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>
            )
        }
    }
    jsx 复制代码
    //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. 配置脚手架代理(配置代理

    js 复制代码
    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组件输入框的内容

      jsx 复制代码
      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. 发送请求

      jsx 复制代码
      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. 动态初始化组件的状态

    jsx 复制代码
    //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>
            )
        }
    }
    jsx 复制代码
    //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. 发起请求并将数据显示到组件上

    jsx 复制代码
    //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>
            )
        }
    }
    jsx 复制代码
    //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. 下载:

    nginx 复制代码
    npm install pubsub-js --save
  3. 使用:

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

    jsx 复制代码
    //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. 初始化组件的状态

    jsx 复制代码
    //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. 发布消息与发送请求数据

    jsx 复制代码
    //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. 订阅消息与动态更新数据

    jsx 复制代码
    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. 老版本浏览器可能不支持
jsx 复制代码
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小知识点:解构赋值+重命名

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

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

    jsx 复制代码
    try{
        const response=await fetch(/api1/search/users2?q=${keyWord))
        const data await response.json()
        console.log(data);
    }catch (error){
        console.1og('请求出错',error);
    }
相关推荐
Boilermaker19921 小时前
【Java EE】SpringIoC
前端·数据库·spring
中微子1 小时前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上10241 小时前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y1 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁2 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry2 小时前
Fetch 笔记
前端·javascript
拾光拾趣录2 小时前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟2 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan2 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson2 小时前
青苔漫染待客迟
前端·设计模式·架构