react setState(updater, [callback]) 改变state对象的值

2,601 阅读3分钟

@[toc]

一、文章参考

  1. react 修改state为对象中的某一个属性值
  2. State & 生命周期
  3. setState API介绍

二、正确地使用 State

setState(updater, [callback]) API介绍

  1. 只传递一个对象(需要改变的state属性)
setState({
	"改变state的key": "新的值"
})
  1. 参数一为带有形式参数的 updater 函数:
setState((state, props) => {
	return {
		"改变state的key": "新的值"
	}
})
  1. setState() 的第二个参数为可选的回调函数 它将在 setState 完成合并并重新渲染组件后执行。通常,我们建议使用 componentDidUpdate() 来代替此方式。

react 想要更新视图只能用 setState( ) 方法

关于 setState() 这里有三件事情需要知道

  1. 不要直接更新状态, 而是使用 setState()

  2. 状态更新是异步的

    React 可以将多个setState() 调用合并成一个调用来提高性能。

  3. this.props 和 this.state 都可能是异步更新的,你不应该依靠它们的值来计算下一个状态。

    请使用第二种形式的 setState() 来接受一个函数而不是一个对象

    该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数

三、案例

3.1 setState()接收对象(修改普通的input 表单输入值)

  1. 使用React.createClass创建React组件
var NoLink = React.createClass({
    getInitialState:function(){
        return {message:''}
    },
    handelChange:function(event){
        console.log(event.target);
        this.setState({message:event.target.value})
    },
    render:function(){
        var mess = this.state.message;
        return (
            <div>
                <input type="text" onChange={this.handelChange} value={mess} />
                <b>{mess}</b>
            </div>
        )
    }
});

React.render(<NoLink />,document.body);
  1. 使用 class 创建组件
import React from 'react'
// 直接引入scss文件,转为了css
import styleObj from './CustomerRegistPage.module.scss'
import { Input, message } from 'antd'

import mimaImg from '@/asserts/images/icons/icon_mima@1x.png'
import chakanmimaImg from '@/asserts/images/icons/icon_chakanmima@1x.png'
import BaseComponent from '@/core/BaseComponent'

export default class CustomerRegistPage extends BaseComponent {
  constructor (props) {
    super(props)
    this.state = {
      studentRePassword: ''
    }
  }
  getJSXImg (imgSrc) {
    return <img src={imgSrc} style={{ width: 24, height: 24 }} />
  }

  // input 表单绑定的事件
  userInputChange (type, event) {
    const value = event.target.value
    this.setState({
      [type]: value
    })
  }
  render () {
    const inputStyle = {
      width: 400,
      height: 44
    }
    const { studentRePassword } = this.state
    return (
      
      <Input
        placeholder="请确认密码"
        value={studentRePassword}
        onChange={this.userInputChange.bind(this, 'studentRePassword')}
        style={inputStyle}
        prefix={this.getJSXImg(mimaImg)}
        suffix={this.getJSXImg(chakanmimaImg)}
      />
    )
  }
}

3.2 setState()接收函数(定时器倒计时)

错误写法

export default class EditSuccess extends BaseComponent {
  constructor (...args) {
    super(...args)

    this.state = {
      timer: null,
      name: 'huangbiao',
      timeCounter: 60
    }
  }

  componentDidMount () {
  	// 第一进来 timeCounter 的值为60
    const { timeCounter } = this.state
    this.state.timer = setInterval(() => {
      // 类似于闭包
	  // 执行定时器的方法,timeCounter 的值为60一直没有变化,所以timeCounter-1 的值为59就不会变化
      this.setState({
        timeCounter: timeCounter - 1
      })
    }, 1000)
  }

  componentWillUnmount () {
    clearInterval(this.state.timer)
  }

  render () {
    const { timeCounter, name } = this.state
    return (
      <div className={styleObj.EditSuccess}>
        {timeCounter}
      </div>
    )
  }
}

闭包,将timeCounter 的值固化了,永远都为 60

将setState()参数改为函数,state代表当前state值的状态,返回值为需要修改的state对象

componentDidMount () {
    this.state.timer = setInterval(() => {
      this.setState((state, props) => {
        return {
          timeCounter: state.timeCounter - 1
        }
      })
    }, 1000)
}

或者,每次都从this.state中获取值,不要采用闭包

componentDidMount () {
    this.state.timer = setInterval(() => {
      this.setState({
        timeCounter: this.state.timeCounter - 1
      })
    }, 1000)
  }

3.3 下拉列表的二级联动,使用setState回调函数

选择行业之后,就要根据选择的行业去查询对应的岗位,因为setState是异步的,无法保证setState字段'industryItemCode'之后值一定发生了变化,就有可能导致getPostListByCode方法中的 industryItemCode 值还是之前没有变化的,因此,触发getPostListByCode方法必须要在industryItemCode变化之后调用

export default class ExpertRegistForm extends BaseComponent {
  constructor (props) {
    super(props)
    this.state = {
      industryItemCode: '', // 选中行业的code
      // 所在行业
      industryArr: [],
      interviewerDetailList: [ // 选择的细分领域
        // {
        //   companyPostName: 'Java',
        //   companyPostId: 3
        // }
      ],
      // 根据行业找到的领域
      postListArr: []
    }
    this.getPostListByCode = this.getPostListByCode.bind(this)
    this.selectIndustryChange = this.selectIndustryChange.bind(this)
  }


  // 根据行业Code获取所有职业信息
  getPostListByCode () {
    const { industryItemCode } = this.state
    getPostListByCodeService(industryItemCode).then((res) => {
      this.setState({
        // postListArr: transformMapTreeData(res.data)
        postListArr: res.data
      })
    })
  }

  // select 表单绑定的事件
  selectIndustryChange (type, value) {
    this.setState({
      [type]: value
    }, () => {
      this.getPostListByCode()
    })
  }


  render () {
    const {
      interviewerDetailList, interviewerUsername, companyName, industryItemName,
      interviewerTagsList,
      industryItemCode, interviewerWorkYear, interviewerEmail, interviewerPassword,
      interviewerRePassword, interviewTimeZone, timeZoneArr, industryArr, postListArr
    } = this.state
    return (
      <>
      <div className={styleObj['ExpertRegistForm--form__item']}>
            {/* <Input placeholder="所在行业" style={inputStyle} prefix={this.getJSXImg(hangyeImg)} /> */}
            <Select
              placeholder="所在行业"
              defaultValue={industryItemCode}
              value={industryItemCode}
              style={{ width: 400 }}
              size="large"
              onChange={this.selectIndustryChange.bind(this, 'industryItemCode')}
            >
              {industryArr.map((item, index) => {
                return (
                  <Option
                    value={item.industryItemCode}
                    key={item.industryItemName}
                  >
                    {item.industryItemName}
                  </Option>
                )
              })}
            </Select>
          </div>
          <div className={styleObj['ExpertRegistForm--form__item']}>
            {/* <Input placeholder="细分领域" style={inputStyle} prefix={this.getJSXImg(xifenImg)} /> */}
            <Select
              mode="multiple"
              placeholder="细分领域"
              defaultValue={interviewerDetailList}
              style={{ width: 400 }}
              size="large"
              onChange={this.userSelectChange.bind(this, 'interviewerDetailList')}
            >
              {postListArr.map((item, index) => {
                return (
                  <Option value={item.industryItemId} key={item.industryItemId}>{item.industryItemName}</Option>
                )
              })}
            </Select>
          </div>
      </>
    )
  }
}