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);
    }
相关推荐
并不会42 分钟前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
悦涵仙子44 分钟前
CSS中的变量应用——:root,Sass变量,JavaScript中使用Sass变量
javascript·css·sass
衣乌安、1 小时前
【CSS】居中样式
前端·css·css3
兔老大的胡萝卜1 小时前
ppk谈JavaScript,悟透JavaScript,精通CSS高级Web,JavaScript DOM编程艺术,高性能JavaScript pdf
前端·javascript
低代码布道师1 小时前
CSS的三个重点
前端·css
耶啵奶膘2 小时前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^4 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie4 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic5 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿5 小时前
webWorker基本用法
前端·javascript·vue.js