美文网首页
React 小书笔记

React 小书笔记

作者: 喵呜Yuri | 来源:发表于2019-03-31 10:38 被阅读0次

http://huziketang.mangojuice.top/books/react/ react小书
这是我读React 小书的一些笔记

开始

只有改变了state状态,也就是使用了this.setState(...)才能导致重新调用render。
记住,只要你要写 React.js 组件,那么就必须要引入这两个东西:React和组件继承 Component 类

import React, { Component } from 'react'

render的用法:

元素变量:

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>
  )
}

条件返回:

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

ReactDOM 可以帮助我们把 React 组件渲染到页面上去,没有其它的作用了。
自定义的组件都必须要用大写字母开头,普通的 HTML 标签都用小写字母开头。
on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上。
<div></div>属于HTML 的标签,<Mytitle></Mytitle>属于组件标签

获取点击实例:e.target

class Title extends Component {
  handleClickOnTitle (e) {
    console.log(e.target.innerHTML)
  }

  render () {
    return (
      <h1 onClick={this.handleClickOnTitle}>React 小书</h1>
    )
  }
}

点击获取实例:this,传参

class Title extends Component {
  handleClickOnTitle (word, e) {
    console.log(this, word)
  }

  render () {
    return (
      <h1 onClick={this.handleClickOnTitle.bind(this, 'Hello')}>React 小书</h1>
    )
  }
}

setState 接受函数参数:

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

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

所以如果你想在 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
  }

上面我们进行了三次 setState,但是实际上组件只会重新渲染一次,而不是三次,这就是setState的合并,并不需要担心多次进行 setState 会带来性能问题。

默认配置 defaultProps:

如果this.props.likedText没有被定义的时候,就会去取defaultProps.likedText

 static defaultProps = {
    likedText: '取消',
    unlikedText: '点赞'
  }

props 不可变:

 handleClickOnLikeButton () {
    this.props.likedText = '取消'
  }

这样的代码会引发报错,props是不可变的,如果 props 渲染过程中可以被修改,那么就会导致这个组件显示形态和行为变得不可预测,这样会可能会给组件使用者带来困惑。
props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。

state和props

尽量少地用 state,尽量多地用 props。
没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。

渲染列表:

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

更简化:

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 key={user.id} user={user} />)}
      </div>
    )
  }
}

记得用key,没有id的话可以用计数器来表示

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

想想使用key的目的,这种方法只是掩耳盗铃

本着React.js 团队的做法,把事情搞复杂一些,提高数据修改的门槛。解决“模块(组件)之间需要共享数据”,和“数据可能被任意修改导致不可预料的结果”之间的矛盾。是action,dispatch出现的重要原因

纯函数:

1.函数的返回结果只依赖于它的参数。
2.函数执行过程里面没有副作用。
实例1:

const a = 1
const foo = (b) => a + b
foo(2) // => 3

这个不是纯函数,因为返回的结果不止和参数有关还和外部变量有关
实例2:

const a = 1
const foo = (obj, b) => {
  obj.x = 2
  return obj.x + b
}
const counter = { x: 1 }
foo(counter, 2) // => 4
counter.x // => 2

foo函数的执行对外部的 counter 产生了影响(obj.x产生了影响),它产生了副作用
除了修改外部的变量,一个函数在执行过程中还有很多方式产生外部可观察的变化,比如说调用 DOM API 修改页面,或者你发送了 Ajax 请求,还有调用 window.reload 刷新浏览器,甚至是 console.log 往控制台打印数据也是副作用。

为什么要煞费苦心地构建纯函数?因为纯函数非常“靠谱”,执行一个纯函数你不用担心它会干什么坏事,它不会产生不可预料的行为,也不会对外部产生影响。不管何时何地,你给它什么它就会乖乖地吐出什么。如果你的应用程序大多数函数都是由纯函数组成,那么你的程序测试、调试起来会非常方便。

es6 扩展运算符赋值

一般我们不会直接改变state而是使用浅复制,创建一个NewState
像这样:

let newAppState1 = { // 新建一个 newAppState1
  ...appState , // 复制 newAppState1 里面的内容
  title: { // 用一个新的对象覆盖原来的 title 属性
    ...appState .title, // 复制原来 title 对象里面的内容
    color: "blue" // 覆盖 color 属性
  }
}

它的好处是什么?我们为什么要这么做?

appState !== newAppState // true,两个对象引用不同,数据变化了,重新渲染
appState.title !== newAppState.title // true,两个对象引用不同,数据变化了,重新渲染
appState.content !== appState.content // false,两个对象引用相同,数据没有变化,不需要重新渲染

高阶函数:

高阶组件就是一个函数,传给它一个组件,在里面通过方法和传入的参数装饰一下,返回一个新的组件。

redux:

要注意的是,Redux 和 React-redux 并不是同一个东西。Redux 是一种架构模式(Flux 架构的一种变种),它不关注你到底用什么库,你可以把它应用到 React 和 Vue,甚至跟 jQuery 结合都没有问题。而 React-redux 就是把 Redux 这种架构模式和 React.js 结合起来的一个库,就是 Redux 架构在 React.js 中的体现。

reducer是纯函数
redux的使用套路总结下来就是:

// 定一个 reducer
function reducer (state, action) {
  /* 初始化 state 和 switch case */
}

// 生成 store
const store = createStore(reducer)

// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))

// 首次渲染页面
renderApp(store.getState()) 

// 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)

connect

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from './react-redux'

class Header extends Component {
  static propTypes = {
    themeColor: PropTypes.string
  }

  render () {
    return (
      <h1 style={{ color: this.props.themeColor }}>React.js 小书</h1>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    themeColor: state.themeColor
  }
}
Header = connect(mapStateToProps)(Header)

export default Header

我们可以这么理解这句话:

Header = connect(mapStateToProps)(Header)

Header中包含对共享数据的操作,以及根据state进行后续组件渲染,connect是一个包装Header组件的装饰函数,它需要这个原组件本身和与组件相关的state数据作为参数。
还要传一个mapDispatchToProps作为参数

const mapDispatchToProps = (dispatch) => {
  return {
    onSwitchColor: (color) => {
      dispatch({ type: 'CHANGE_COLOR', themeColor: color })
    }
  }
}
ThemeSwitch = connect(mapStateToProps, mapDispatchToProps)(ThemeSwitch)

挂载阶段的生命周期:

我们把 React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程称为组件的挂载(这个定义请好好记住)

-> constructor()
-> componentWillMount()
-> render()
// 然后构造 DOM 元素插入页面
-> componentDidMount()

一些组件启动的动作,包括像 Ajax 数据的拉取操作、一些定时器的启动等,就可以放在 componentWillMount 里面进行
这里有段计时器的代码:

class Clock extends Component {
  constructor () {
    super()
    this.state = {
      date: new Date()
    }
  }

  componentWillMount () {
  clearInterval(this.timer);//记得先清除定时器,避免重新渲染时定时器还在
    this.timer = setInterval(() => {
      this.setState({ date: new Date() })
    }, 1000)
  }
  ...
}

更新阶段的组件生命周期:

1.shouldComponentUpdate(nextProps, nextState):你可以通过这个方法控制组件是否重新渲染。如果返回 false 组件就不会重新渲染。这个生命周期在 React.js 性能优化上非常有用。
2.componentWillReceiveProps(nextProps):组件从父组件接收到新的 props 之前调用。
3.componentWillUpdate():组件开始重新渲染之前调用。
4.componentDidUpdate():组件重新渲染并且把更改变更到真实的 DOM 以后调用。

ref:

React.js 并不能完全满足所有 DOM 操作需求,有些时候我们还是需要和 DOM 打交道。比如说你想进入页面以后自动 focus 到某个输入框,你需要调用 input.focus() 的 DOM API,比如说你想动态获取某个 DOM 元素的尺寸来做后续的动画,等等

class AutoFocusInput extends Component {
  componentDidMount () {
    this.input.focus()
  }

  render () {
    return (
      <input ref={(input) => this.input = input} />
    )
  }
}

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

然后我们就可以在 componentDidMount 中使用这个 DOM 元素,并且调用 this.input.focus() 的 DOM API。整体就达到了页面加载完成就自动 focus 到输入框的功能
也可以给组件加:

<Clock ref={(clock) => this.clock = clock} />

但ref这个属性能不用就不要用

props.children:

它定义了一种外层结构形式,然后你可以往里面塞任意的内容。有点类似于DOM操作中的append()

class PropsChilden extends Component {
    render() {
       const childer = this.props.children;
        return (
            <div>
                {childer.map((item,index)=>{
                return <div key={index}>{item}</div>;
                })}
            </div>

        );
    }

}

export default PropsChilden;

用的时候:

 <PropsChilden>
              <span>rick</span>
              <span>zhangamie</span>
              <span>react</span>
              22132132
 </PropsChilden>

但是这样是不可以的:

 <PropsChilden>
            [1,2,3,4]
 </PropsChilden>

因为传过去的this.props.children是string类型,map函数自然会报错

动态样式:

style接受的是一个对象,要用驼峰命名方式,font-size写做fontSize

<h1 style={{fontSize: '12px', color: this.state.color}}>React.js 小书</h1>

dangerouslySetInnerHTML

class Editor extends Component {
  constructor() {
    super()
    this.state = {
      content: '<h1>React.js 小书</h1>'
    }
  }

  render () {
    return (
      <div className='editor-wrapper'>
        {this.state.content}
      </div>
    )
  }
}

当你写这些以为会得到一个加粗黑体的一行字吗?不,你得到的是这个:


image.png

你应该这么写:

  render () {
    return (
      <div
        className='editor-wrapper'
        dangerouslySetInnerHTML={{__html: this.state.content}} />
    )
  }

但是设置 innerHTML 可能会导致跨站脚本攻击(XSS),这个属性不必要的情况就不要使用。

PropTypes 和组件参数验证

在做大型应用的时候有PropTypes写作规范是很有益的,因为js是弱类型语言,是允许let a = {number:1};a = 1;这样的代码的。项目越大有些隐晦的bug会越不好找。我们需要一个规范及时矫正偏差
安装:npm install --save prop-types

import React, { Component } from 'react'
import PropTypes from 'prop-types'

class Comment extends Component {
  static propTypes = {
    comment: PropTypes.object
  }

  render () {
    const { comment } = this.props
    return (
      <div className='comment'>
        <div className='comment-user'>
          <span>{comment.username} </span>:
        </div>
        <p>{comment.content}</p>
      </div>
    )
  }
}

这段代码是说:comment字段必须是一个object类型,如果你传入其他类型就会报错


image.png

也有该字段未赋值,undefined,我们可以这样写:

static propTypes = {
  comment: PropTypes.object.isRequired
}

相关文章

网友评论

      本文标题:React 小书笔记

      本文链接:https://www.haomeiwen.com/subject/rsjjbqtx.html