React.js 小书 阅读笔记4

仅供个人学习!
本文作者:胡子大哈
本文原文:http://huziketang.com/books/react/blog/lesson4


注意: setTimeout(code,millisec)

setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。

code必需。要调用的函数后要执行的 JavaScript 代码串。
millisec必需。在执行代码前需等待的毫秒数。

必须要用一个外层的 JSX 元素把所有内容包裹起来。返回并列多个 JSX 元素是不合法的

举例:错误的做法:

render () {
  return (
    <div>第一个</div>
    <div>第二个</div>
  )
}
...

正确的是:

  <div>
      <div>第一个</div>
      <div>第二个</div>
    </div>

在 JSX 当中你可以插入 JavaScript 的表达式,表达式返回的结果会相应地渲染到页面上。表达式用 {} 包裹。 

举例:

render () {
  const word = 'is good'
  return (
    <div>
      <h1>React 小书 {word}</h1>
    </div>
  )
}

改成

{1 + 2}或

 {(function () { return 'is good'})()}

 

总之:{} 内可以放任何 JavaScript 的代码,包括变量、表达式计算、函数执行等等。 render 会把这些代码返回的内容如实地渲染到页面上

这点也可以用在属性里

注意:直接使用 class 在 React.js 的元素上添加类名如 <div class=“xxx”> 这种方式是不合法的。因为 class 是 JavaScript 的关键字,所以 React.js 中定义了一种新的方式:className 来帮助我们给元素添加类名。

还有一个特例就是 for 属性,例如 <label for='male'>Male</label>,因为 for 也是 JavaScript 的关键字,所以在 JSX 用 htmlFor 替代,即 <label htmlFor='male'>Male</label>。而其他的 HTML 属性例如 styledata-* 等就可以像普通的 HTML 属性那样直接添加上去。

 


结合一下,我们可以做到显示或者隐藏某些元素:

例:

render () {
  const isGoodWord = true
  return (
    <div>
      <h1>
        React 小书
        {isGoodWord
          ? <strong> is good</strong>
          : null//根据值的不同可能显示可能隐藏
        }
      </h1>
    </div>
  )
}
...

————————————也可以有个升级版:

...
render () {
  const isGoodWord = true
  const goodWord = <strong> is good</strong>
  const badWord = <span> is not good</span>
  return (
    <div>
      <h1>
        React 小书
        {isGoodWord ? goodWord : badWord}
      </h1>
    </div>
  )
}
...

——————这个有点难理解

...
renderGoodWord (goodWord, badWord) {
  const isGoodWord = true
  return isGoodWord ? goodWord : badWord
}

render () {
  return (
    <div>
      <h1>
        React 小书
        {this.renderGoodWord(
          <strong> is good</strong>,
          <span> is not good</span>//有点像解构赋值那种 只不过赋的是组件
        )}
      </h1>
    </div>
  )
}
...

组件的组合、嵌套和组件树

注意:组件标签也是要关闭的   <Title />

这样可复用性非常强,我们可以把组件的内容封装好,然后灵活在使用在任何组件内。

自定义的组件都必须要用大写字母开头,普通的 HTML 标签都用小写字母开头。

例:

<div>
  <Title />
  <Title />
  <Title />
</div>

假设页面是由 Header 、Main 、Footer 几个部分组成,由一个 Index 把它们组合起来。


层层包裹的关系

class Title extends Component {
  render () {
    return (
      <h1>React 小书</h1>
    )
  }
}

class Header extends Component {
  render () {
    return (
    <div>
      <Title />
      <h2>This is Header</h2>
    </div>
    )
  }
}

class Main extends Component {
  render () {
    return (
    <div>
      <h2>This is main content</h2>
    </div>
    )
  }
}

class Footer extends Component {
  render () {
    return (
    <div>
      <h2>This is footer</h2>
    </div>
    )
  }
}

class Index extends Component {
  render () {
    return (
      <div>
        <Header />
        <Main />
        <Footer />
      </div>
    )
  }
}

ReactDOM.render(
  <Index />,
  document.getElementById('root')
)

事件监听

class Title extends Component {
  handleClickOnTitle () {
    console.log('Click on title.')
  }

  render () {
    return (
      <h1 onClick={this.handleClickOnTitle}>React 小书</h1>
//this.***指向一个组件
    )
  }
}

在 React.js 不需要手动调用浏览器原生的 addEventListener 进行事件监听。React.js 帮我们封装好了一系列的 on* 的属性,当你需要为某个元素监听某个事件的时候,只需要简单地给它加上 on* 就可以了。而且你不需要考虑不同浏览器兼容性的问题,React.js 都帮我们封装好这些细节了。

https://reactjs.org/docs/events.html#supported-events    还有很多现成事件  这些事件属性名都必须要用驼峰命名法

这些 on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上。以后会学如何绑定起来用到组件上


关于事件中的 this

一般在某个类的实例方法里面的 this 指的是这个实例本身。但是你在上面的 handleClickOnTitle 中把 this 打印出来,你会看到 this 是 null 或者 undefined

这是因为 React.js 调用你所传给它的方法的时候,并不是通过对象方法的方式调用(this.handleClickOnTitle),而是直接通过函数调用 (handleClickOnTitle),所以事件监听函数内并不能通过 this 获取到实例。

如果你想在事件函数当中使用当前的实例,你需要手动地将实例方法 bind 到当前实例上再传入给 React.js。

//改成以下形式
render () {
    return (
      <h1 onClick={this.handleClickOnTitle.bind(this)}>React 小书</h1>
    )
  }
}

bind 不仅可以帮我们把事件监听方法中的 this 绑定到当前组件实例上;还可以帮助我们在在渲染列表元素的时候,把列表元素传入事件监听函数当中

 


组件的 state 和 setState

注意,当我们要改变组件的状态的时候,不能直接用 this.state = xxx 这种方式来修改,如果这样做 React.js 就没办法知道你修改了组件的状态,它也就没有办法更新页面。所以,一定要使用 React.js 提供的 setState 方法,它接受一个对象或者函数作为参数

当你调用 setState 的时候,React.js 并不会马上修改 state。例:

...
  handleClickOnLikeButton () {
    console.log(this.state.isLiked)
    this.setState({
      isLiked: !this.state.isLiked
    })
    console.log(this.state.isLiked)
  }
...

你会发现两次打印的都是 false,即使我们中间已经 setState 过一次了。这并不是什么 bug,只是 React.js 的 setState 把你的传进来的状态缓存起来,稍后才会帮你更新到 state 上,所以你获取到的还是原来的 isLiked

 

 

所以我们想要一种能实时更新的方法:

这里就自然地引出了 setState 的第二种使用方式,可以接受一个函数作为参数。React.js 会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象:

例:

...
  handleClickOnLikeButton () {
    this.setState((prevState) => {
      return { count: 0 }
    })
    this.setState((prevState) => {
      return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
    })
    this.setState((prevState) => {
      return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
    })
    // 最后的结果是 this.state.count 为 3
  }
...

【也就是设置一个变量】

 在使用 React.js 的时候,并不需要担心多次进行 setState 会带来性能问题。


配置组件的 props

怎么把 props 传进去呢?在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为 props 对象的键值

例:

class Index extends Component {
  render () {
    return (
      <div>
        <LikeButton likedText='已赞' unlikedText='赞' />
      </div>
    )
  }
}

现在我们把 likedText 和 unlikedText 这两个参数封装到一个叫 wordings 的对象参数内,然后传入点赞组件中。 

例:

class Index extends Component {
  render () {
    return (
      <div>
        <LikeButton wordings={{likedText: '已赞', unlikedText: '赞'}} />
      </div>
    )
  }
}

{{}} 就是在 {} 内部用对象字面量返回一个对象而已。 

 

class LikeButton extends Component {
  constructor () {
    super()
    this.state = { isLiked: false }
  }

  handleClickOnLikeButton () {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }

  render () {
    const wordings = this.props.wordings || { // ||实现默认配置
      likedText: '取消',
      unlikedText: '点赞'
    }
    return (
      <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked ? wordings.likedText : wordings.unlikedText} ?
      </button>
    )
  }
}

 甚至可以往组件内部传入函数作为参数:

class Index extends Component {
  render () {
    return (
      <div>
        <LikeButton
          wordings={{likedText: '已赞', unlikedText: '赞'}}
          onClick={() => console.log('Click on like button!')}/>
      </div>
    )
  }
}

这样可以通过 this.props.onClick 获取到这个传进去的函数,

...
  handleClickOnLikeButton () {
    this.setState({
      isLiked: !this.state.isLiked
    })
    if (this.props.onClick) {
      this.props.onClick()
    }
  }
...

一个组件的行为、显示形态都可以用 props 来控制,就可以达到很好的可配置性。

默认配置 defaultProps

defaultProps 作为点赞按钮组件的类属性,里面是对 props 中各个属性的默认配置。

这样我们就不需要判断配置属性是否传进来了:如果没有传进来,会直接使用 defaultProps 中的默认属性。

所以可以看到,在 render 函数中,我们会直接使用 this.props 而不需要再做判断。

class LikeButton extends Component {
  static defaultProps = {
    likedText: '取消',
    unlikedText: '点赞'
  }

  constructor () {
    super()
    this.state = { isLiked: false }
  }

  handleClickOnLikeButton () {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }

  render () {
    return (
      <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked
          ? this.props.likedText
          : this.props.unlikedText} ?
      </button>
    )
  }
}

props 不可变

props 一旦传入进来就不能改变。

你不能改变一个组件被渲染的时候传进来的 props

但这并不意味着由 props 决定的显示形态不能被修改。组件的使用者可以主动地通过重新渲染的方式把新的 props 传入组件当中。

由于 setState 会导致 Index 重新渲染,达到更新状态的效果。

class Index extends Component {
  constructor () {
    super()
    this.state = {
      likedText: '已赞',
      unlikedText: '赞'
    }
  }

  handleClickOnChange () {
    this.setState({
      likedText: '取消',
      unlikedText: '点赞'
    })
  }

  render () {
    return (
      <div>
        <LikeButton
          likedText={this.state.likedText}
          unlikedText={this.state.unlikedText} />
        <div>
          <button onClick={this.handleClickOnChange.bind(this)}>
            修改 wordings
          </button>
        </div>
      </div>
    )
  }
}

题目:

完成两个组件,电脑 Computer 和显示器 Screen

电脑有个 status 状态表示电脑现在是开还是关的,status 为 on 为开,status 为 off 为关,默认状态为 off。电脑有个按钮,点击可以自由切换电脑的开关状态。

显示器接受一个名为 showContent 的 props,显示器会把它内容显示出来。如果不传入 showContent,显示器显示 “无内容”。

电脑包含显示器,当电脑状态为开的时候显示器显示“显示器亮了”,否则显示“显示器关了”。

export default class Computer extends Component {
  constructor () {//构造函数之绑定一下
    super()
    this.state = { status : 'off' }//设置初始状态
  }
  
  handleClickOnButton(){//点击的函数
    this.setState({
      status:this.state.status==='off'?'on':'off'//状态改变
    });
  }
  
  render () {
    return (
      <div>//渲染组件
        <Screen showContent={this.state.status==='on'?'显示器亮了':'显示器关了'}/>
//自定义组件 驼峰命名 Screen  并且改变showcontent参数
        <button onClick={this.handleClickOnButton.bind(this)}>开关</button>//开关组件
      </div>
    )
  }
}

class Screen extends Component {
  static defaultProps = {//设置默认参数
    showContent : '无内容'
  }
  render () {
    return (
      <div className='screen'>{this.props.showContent}</div>//渲染一个组件 显示参数
    )
  }
}


关于 state 和 props 的总结。

state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState方法进行更新,setState 会导致组件的重新渲染。

props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。

state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。

尽量少地用 state,尽量多地用 props

函数式组件就是一种只能接受 props和提供 render 方法的类组件。这个不建议使用。。。


渲染列表数据

有一个用户列表数据,

const users = [
  { username: 'Jerry', age: 21, gender: 'male' },
  { username: 'Tomy', age: 22, gender: 'male' },
  { username: 'Lily', age: 19, gender: 'female' },
  { username: 'Lucy', age: 20, gender: 'female' }
]

之前说过 JSX 的表达式插入 {} 里面可以放任何数据,如果我们往 {} 里面放一个存放 JSX 元素的数组会怎么样?

如果你往 {} 放一个数组,React.js 会帮你把数组里面一个个元素罗列并且渲染出来

 render () {
    return (
      <div>
        {[
          <span>React.js </span>,
          <span>is </span>,
          <span>good</span>
        ]}
      </div>
    )
  }

用循环把元素渲染到页面上:循环上面用户数组里面的每一个用户,为每个用户数据构建一个 JSX,然后把 JSX 放到一个新的数组里面,再把新的数组插入 render 方法的 JSX 里面

const users = [
  { username: 'Jerry', age: 21, gender: 'male' },
  { username: 'Tomy', age: 22, gender: 'male' },
  { username: 'Lily', age: 19, gender: 'female' },
  { username: 'Lucy', age: 20, gender: 'female' }
]

class Index extends Component {
  render () {
    const usersElements = [] // 保存每个用户渲染以后 JSX 的数组
    for (let user of users) {
      usersElements.push( // 循环每个用户,构建 JSX,push 到数组中
        <div>
          <div>姓名:{user.username}</div>
          <div>年龄:{user.age}</div>
          <div>性别:{user.gender}</div>
          <hr />
        </div>
      )
    }

    return (
      <div>{usersElements}</div>
    )
  }
}

ReactDOM.render(
  <Index />,
  document.getElementById('root')
)

这里用了一个新的数组 usersElements,然后循环 users 数组,为每个 user 构建一个 JSX 结构,然后 push 到 usersElements 中。然后直接用表达式插入,把这个 userElements 插到 return 的 JSX 当中。因为 React.js 会自动化帮我们把数组当中的 JSX 罗列渲染出来,所以可以看到页面上显示


但我们一般不会手动写循环来构建列表的 JSX 结构,可以直接用 ES6 自带的 map

class Index extends Component {
  render () {
    return (
      <div>
        {users.map((user) => {//.map()遍历
          return (
            <div>
              <div>姓名:{user.username}</div>
              <div>年龄:{user.age}</div>
              <div>性别:{user.gender}</div>
              <hr />
            </div>
          )
        })}
      </div>
    )
  }
}

现在进一步把渲染单独一个用户的结构抽离出来作为一个组件,继续优化代码:

const users = [
  { username: 'Jerry', age: 21, gender: 'male' },
  { username: 'Tomy', age: 22, gender: 'male' },
  { username: 'Lily', age: 19, gender: 'female' },
  { username: 'Lucy', age: 20, gender: 'female' }
]

class User extends Component {
  render () {
    const { user } = this.props
    return (
      <div>
        <div>姓名:{user.username}</div>
        <div>年龄:{user.age}</div>
        <div>性别:{user.gender}</div>
        <hr />
      </div>
    )
  }
}

class Index extends Component {
  render () {
    return (
      <div>
        {users.map((user) => <User user={user} />)}//js写在标签里要用{}括起来
      </div>
    )
  }
}

ReactDOM.render(
  <Index />,
  document.getElementById('root')//作用:渲染到页面上 root是index.html里的
)

负责展示用户数据的 JSX 结构抽离成一个组件 User ,并且通过 props 把 user 数据作为组件的配置参数传进去;这样改写 Index 就非常清晰了,看一眼就知道负责渲染 users 列表,而用的组件是 User


key

是处理列表元素的复用性会有一个问题:元素可能会在一个列表中改变位置。

<div>a</div>
<div>b</div>
<div>c</div>
//变成
<div>a</div>
<div>c</div>
<div>b</div>

c 和 b 的位置互换了。但其实 React.js 只需要交换一下 DOM 位置就行了,但是它并不知道其实我们只是改变了元素的位置,但如果给每个元素加上唯一的标识,React.js 就可以知道这两个元素只是交换了位置:这时候就用到key了

<div key='a'>a</div>
<div key='b'>b</div>
<div key='c'>c</div>

对于用表达式套数组罗列到页面上的元素,都要为每个元素加上 key 属性,这个 key 必须是每个元素唯一的标识。一般来说,key 的值可以直接后台数据返回的 id,因为后台的 id 都是唯一的。 

这时候可以利用循环赋key值

 return (
      <div>
        {users.map((user, i) => <User key={i} user={user} />)}
      </div>

记住一点:在实际项目当中,如果你的数据顺序可能发生变化,标准做法是最好是后台数据返回的 id 作为列表元素的 key


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值