React复习(3)

(1)类组件中this指向

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date().toLocaleTimeString(),
    }
    //5:创建一个新的绑定好this的函数绑定到this.go属性上
    //5:只绑定一次,不必多次绑定解绑,消耗内存
    this.go = this.go.bind(this)
  }
  render() {
    return (
      <div>
        <h1>Clock</h1>
        <h2>{this.state.time}</h2>
        {/*1:点击时才会调用go函数*/}
        <button onClick={this.go}>Go1</button>
        
        {/*2:页面进行到此处时会直接执行go函数,虽然能把this指向实例但会重复调用render造成死循环*/}
        {/*<button onClick={this.go()}>Go2</button>*/}
        
        {/*3:箭头函数的this指向外围的render,render里的this又指向Clock,能够调用setState*/}
        {/*但是箭头函数会重复声明,占用内存很大,性能不好,而且代码混乱,臃肿*/}
        <button onClick={()=>{
          this.setState({
            time: new Date().toLocaleTimeString(),
          })
        }}>Go3</button>
        
        {/*4:用bind改变this指向*/}
        {/*每次渲染都重复绑定,重复生成新的函数*/}
        <button onClick={this.go.bind(this)}>Go4</button>
        
        {/*5:一次性绑定,当执行该步骤前this已经绑定好了,在constructor里绑定*/}
        <button onClick={this.go}>Go5</button>
        {/*6;ES7写法*/}
        <button onClick={this.goES7}>Go6</button>
        
        <button onClick={() => {
          ReactDOM.unmountComponentAtNode(document.getElementById('app'))
        }
        }>remove</button>
      </div>
    )
  }
  go() {
    //this指向undefined,不能调用setState
    this.setState({
      time: new Date().toLocaleTimeString()
    })
  }
  //ES7新写法,解决this指向最优解
  goES7=()=> {
    this.setState({
      time: new Date().toLocaleTimeString()
    })
  }
  componentDidMount() {
    //钩子函数中的this指向组件实例也就是Clock
    // this.timerId = setInterval(() => {
    //   this.setState({
    //     time: new Date().toLocaleTimeString()
    //   })
    // }, 1000)
  }
  componentWillUnmount() {
    clearInterval(this.timerId)
  }
}
ReactDOM.render(<Clock />, document.getElementById("app"))

钩子函数中的this指向组件实例,解决类组件中this指向问题的方法共有七种,最优解是ES7写法,其次是一次性绑定方法(上面代码中的第五种方法)

(2)setState同步与异步问题

class App extends React.Component{
  state = {a:0,b:0,c:0}
  handleClick=()=>{
    //1:直接修改数据,无法触发render,不能重新渲染
    this.state.a = 1
    //2:setState修改数据,在合成事件中是异步的
    this.setState({a:1})
    console.log(this.state.a);
    //4:多次使用setState都是异步,不是遇到一个立即执行,会合并,只执行一次render,批量更新
    //批量更新的时机在render之前
    this.setState({a:1})
    this.setState({b:1})
    this.setState({c:1})
    console.log(this.state);
    //5:累加也是异步的,不是立即执行
    //当调用时,不能依赖上一个状态来计算下一个状态
    this.setState({a:1})
    this.setState({a:this.state.a+1})
    this.setState({a:this.state.a+1})
    //解决方案
    perState是上次的状态值
    this.setState((perState)=>{
      return {a:perState.a+1}
    })
    //源码中setState(obj,callback),其中obj可以是函数也可以是对象
    //callback就是一个回调函数,所以可以用callback取值
    //6:通过callback来取出异步结果
    this.setState((perState,props)=>({a:perState.a+1}),()=>{
      console.log('callback',this.state.a);
    })
    //7:定时器中setState是同步的,同步事件不会合并,不会有依赖问题
    setTimeout(()=>{
      this.setState({
        a:1
      })
      console.log(this.state.a);
    },1000)
  }
  render(){
    console.log('render');
    return (
      <div>
        <h1>a:{this.state.a}</h1>
        <h1>b:{this.state.b}</h1>
        <h1>c:{this.state.c}</h1>
        {/*合成事件(事件代理,委托)*/}
        <button onClick={this.handleClick}>button1</button>
      </div>
    )
  }
  componentDidMount(){
    console.log('componentDidMount');
    //3:钩子函数里也是异步的
    this.setState({
      a:1
    })
    console.log(this.state.a);
  }
  
}
ReactDOM.render(<App/>,document.getElementById('app'))

在合成事件中,除setState在setTimeout和setInterval中是同步的,其他情况下都是异步的;钩子函数中也是异步的;但是在原生事件中,setState是同步的

(3)解决异步方案

function sum(a,b){
    setTimeout(()=>{
        return a+b
    },2000)
}
var a =sum(1,2)
console.log(a)//undefined
//1:回调函数:给函数传一个函数并在指定的时机去执行
function sum(a,b,callback){
    let result = a+b
    setTimeout(()=>{
      callback(result)
    },2000)
}
function callback(result){
    console.log(result);
}
sum(1,2,callback)//打印出3
var a = sum(1,2,callback)
console.log(a);//undefined
//2:promise对象解决
function sum(a, b) {
  return new Promise((resolve, reject) => {
    let result = a + b
    setTimeout(() => {
      resolve(result)
    }, 2000)
  })
}
//then实际上就是实现result,catch实际上是reject
sum(1, 2).then((result) => {
  console.log(result);
}).catch(() => {
  console.log(err);
})

利用promise解决setState问题

class App extends React.Component {
  state = { a: 0, b: 0, c: 0 }
  handleClick = () => {
    var result = this.setState({ a: 1 })
    console.log(result); //undefined
    this.setState({ a: 1 }, () => {
      console.log('callback1', this.state.a);
    })
  }
  //await在async里,所以必须再此添加async
  handleClickAsync = async () => {
    //通过then的方式来取值
    this.setStatePromise({
        a:1
    })
    this.setStatePromise(10)
     this.setStatePromise((preState,props)=>{
         return {a:preState.a + 2}
     })
    .then(()=>{
        console.log('promise获取值:',this.state.a);
    })
    .catch(err=>{
        console.log(err);
    })

    //3:通过async await解决
    await this.setStatePromise({
        a:1
    })
    //捕获错误后不报红
    try{
      await this.setStatePromise(111)
      console.log(this.state.a);
    }
    catch(error){
      console.log("捕获",error);
    }
  }
  //封装方法
  setStatePromise = (newState) => {
    //函数返回值是一个promise对象
    return new Promise((resolve, reject) => {
      if (typeof newState === 'object' || typeof newState === 'function') {
        //调用setState
        this.setState(newState, resolve())
      } else {
        reject('setStatePromise 参数只能是对象或者函数')
      }
    })
  }
  render() {
    return (
      <div>
        <h2>a:{this.state.a}</h2>
        <h2>b:{this.state.b}</h2>
        <h2>c:{this.state.c}</h2>
        <button onClick={this.handleClick}>button</button>
        <button onClick={this.handleClickAsync}>buttonAsync</button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('app'))

await函数会同步代码等待异步代码先执行,这样就能拿到值了

(4)简易登陆和退出切换

function Welcome(props) {
  return <h1>Welcome</h1>
}
function Login(props) {
  return <h1>Please Login</h1>
}
function Greeting(props) {
  const isLogin = props.isLogin
  console.log("Greeting触发");
  if (isLogin) {
    return <Welcome />
  }
  else {
    return <Login />
  }
}
function LoginButton(props) {
  console.log('函数组件登陆按钮触发');
  return <button onClick={props.login}>Login</button>
}
function LogoutButton(props) {
  console.log('函数组件退出按钮触发');
  return <button onClick={props.logout}>Logout</button>
}
class LoginControl extends React.Component {
  state = { isLogin: true }
  handleLogin = () => {
    console.log('handleLogin');
    this.setState({
      isLogin: true
    })
  }
  handleLogout = () => {
    console.log('handleLogout');
    this.setState({
      isLogin: false
    })
  }
  render() {
    //在此处获取状态,在判断时使用
    const isLogin = this.state.isLogin
    return (
      <div>
        <Greeting isLogin={this.state.isLogin} />
        {
          //此处的状态为上文const变量获取的状态,登陆状态下显示logout,退出状态下显示login
          isLogin ? <LogoutButton logout={this.handleLogout} /> : <LoginButton login={this.handleLogin} />
        }
      </div>
    )
  }
}
let vDOM = <LoginControl />
ReactDOM.render(vDOM, document.getElementById('app'))

 判断是否登陆内部处理顺序为:已经登陆,显示退出按钮,将在LoginControl组件中定义的事件传播给函数组件LogoutButton,渲染出LogoutButton;当退出按钮被点击时,触发事件,改变state的值,变为退出状态,render重新渲染,显示登陆按钮,将LoginControl组件中定义的事件传播给函数组件LoginButton;点击登陆按钮时有进行循环

(5)map和key

// map:取出数组每一项进行逻辑处理然后返回一个新的数组
// key要加在离数据源最近的地方,在兄弟节点之间具有唯一性,全局下可以不唯一
let source = [1, 2, 3, 4, 5]
let double = source.map(item => item * 2)
function ListItem(props) {
  let { value } = props
  return (
    <li>{value * 2}</li>
  )
}
function ListContainer(props) {
  let { data } = props
  //通过value把值传到ListItem
  let listItems = data.map(item => <ListItem key={item} value={item} />)
  return (
    <ul>
      {listItems}
    </ul>
  )
}
ReactDOM.render(<ListContainer data={source} />, document.getElementById('app'))

(6)标题与内容练习

function Content(props) {
      let { title, content } = props
      return (
        <div>
          <h3>{title}</h3>
          <h3>{content}</h3>
        </div>
      )
    }
    function Title(props) {
      let { title } = props
      return (
        <li>{title}</li>
      )
    }
    function Blogs(props) {
      let { posts } = props
      let sidebar = (
        <ul>
          {
            //此处对数据已经处理完了,所以存到变量里,直接使用就行了
            posts.map(item => <Title key={item.id} title={item.title} />)
          }
        </ul>
      )
      let content = posts.map(item => <Content key={item.id} title={item.title} content={item.content} />)
      //变量是数组,自动遍历了
      return (
        <div>
          <div>{sidebar}</div>
          <div>{content}</div>
        </div>
      )
    }
    const posts = [
      { id: 1, title: 'title1', content: 'welcome to harbin' },
      { id: 2, title: 'title2', content: 'welcome to mdj' },
    ]
    ReactDOM.render(<Blogs posts={posts} />, document.getElementById('app'))

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无非是我

不必打赏,欢迎指正

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

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

打赏作者

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

抵扣说明:

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

余额充值