React提高01 遇到的坑

react

总结整理了一下一年前使用React开发测评平台时的经验。

总共使用React开发了一个项目,用了1个多月,学到的东西比这一年都多,值得好好总结。

已同步到个人博客,欢迎访问


定时任务中的setState

如果定时任务触发时,组件已经被销毁,会给出警告

setState(...): Can only update a mounted or mounting component.

虽然只是一个warning,但是还是证明写的代码不规范,不一定什么时候就埋坑了。

这个问题实质是:setState在异步的callback里执行,而这个时候由于返回上一页,组件已经被销毁了。

isMounted方法做判断是官方不推荐的方法,而且我也不知道怎么实现。

真正的解决方法应该是在componentWillUnmount中将事件清除或者变量设置为null

对于setInterval来说需要在componentWillUnmountclear

bindaddEventListener中的使用

在组件中添加了scroll事件:

window.addEventListener('scroll', this.windowScroll.bind(this));

componentWillUnmount中想要清除绑定的事件:

window.removeEventListener('scroll', this.windowScroll.bind(this));

这样做是不会生效的。

因为bind(this)方法总会返回一个新的函数,所以在removeEventListener时,移除的是一个不存在的、新的函数。

解决方法是在constructor里面对windowScroll一次性绑定this。(这样this.windowScroll变量指向的就是同一个bind之后的方法了。

export default class Overview extends React.Component {

constructor(props) {

super(props);

this.windowScroll = this.windowScroll.bind(this)

}

windowScroll(e) {

// do something

}

componentDidMount() {

window.addEventListener('scroll', this.windowScroll);

}

componentWillUnmount() {

window.removeEventListener('scroll', this.windowScroll);

}

// ...

}

globalStore中的方法在组件中不能直接调用

const { globalStore } = this.props;

const { setBtnStatus } = globalStore;

globalStore.setBtnStatus(); // OK

setBtnStatus(); // 报错

这是因为this指向调用者,前者的this指向globalStore,后者指向window

checkbox值不能正确重置

在不同题目之间跳转时checkbox值不会清除,原因是在跳转时即使input所在的组件被重新render,但是如果input本身的key没有变化,React就认为这个组件整体没有变化,不会重新渲染,只会对input局部渲染,所以inputvalue的值就不会重置。

只有key值变化,React才会认为组件整体变化,整体渲染

解决方法是给input加上key的属性,根据页面变化,强制重新渲染,然后在componentWillReceiveProps里面对选中的答案状态进行重置

并且,不能在componentWillUpdatecomponentShouldUpdate里面对state的值进行控制,会造成死循环

(2017.07.14更新)

当时查资料的时候对这里理解的不全面,原文的意思是如果key值太简单,例如只有数字序号作为key,当项目发生变化,key值可能不变,React可能会认为是同一个组件而不进行渲染。所以上面提到的:

只有key值变化,React才会认为组件整体变化,整体渲染

不够准确,应该key值独一无二化,例如用ID来标识key,这样key值不重复,就不会发生不渲染的情况。

(2019.01.22更新)

有两个问题:

(1)React中的key并不要求全局唯一,因为key的作用域是当前列表内,同一个列表内唯一即可,不同列表、不同组件间都不需要考虑这个问题。

(2)第二个是,key值是加可以在包含input的组件的(由于当时的组件划分的不合理所以只能加在input上)。

当组件上没有key时,传入的Prop发生变化,React会寻求复用,保存组件状态,不会触发Mount系列事件,只会触发Update系列事件;

而如果增加了key,当传入的Prop发生变化也会导致组件重新渲染,所有状态重置

因此就有两个处理方法:一个就是不为组件增加key,而是在更新周期的componentWillReceiveProps中寻求状态重置,另一种就是为组件增加key,有React自动完成重置

前者在逻辑不复杂的情况下是可以使用的,但是如果逻辑比较复杂就会导致大量的逻辑和函数在componentWillReceiveProps中,而后者就一劳永逸了,直接销毁了组件并重建,在componentDidMount中处理重置好,可以参考这篇文章。

其实在Vue中也是相同的原理。

Prop的类型验证和默认值

export default class Question extends React.Component {

// 类型验证

static propTypes = {

finishBtnDisabled: PropTypes.bool

};

// Prop默认值

static defaultProps = {

finishBtnDisabled : false,

};

}

对Mobx的Store中的变量赋值

let { spendMinute } = timeStore;

spendMinute = 100;

这样是不行的,这是声明了新的变量,有了两种方法:

(1)直接引用Store中的变量,这种方法在Mobx的严格模式下第一种方法是不允许的

timeStore.spendMinute = 100

(2)引用Store中的方法,对变量赋值(推荐)

// Store中

export default class Mark {

@observable spendMinute = 0;

@action changeSpendMinute(minute) {

this.spendMinute = minute

}

}

// 组件中

@observer

export default class Overview extends React.Component {

changeMinute() {

const { globalStore } = this.props;

globalStore.changeSpendMinute(100);

}

}

<div>blur事件

会遇到这样的需求:当标签失去焦点时,将菜单隐藏并触发一些操作,直觉就想到使用focusblur事件。

但是这两个事件只对form表单控件有效,但是对于<span><div>等普通元素并不生效

解决方法就是设置这些元素的tabindex属性,就可以触发焦点事件了。

<div onBlur={this.blurHandler.bind(this)} 

onFocus={this.focusHandler.bind(this)}

tabIndex="1">

blur

</div>

此外,如果希望点击出现的菜单本身不会在点击自己时,因为blur事件消失,需要将菜单放入被点击事项的子元素中。

DOM事件传参

DOM事件传参,事件对象作为最后一个参数并传入到事件处理程序中

<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

与Vue中是不同的。

Vue中的点击事件传参时,需要手动将$event传入,否则事件处理程序无法访问事件对象。不传参时,事件处理程序默认的参数就是事件对象。

React中的this

React组件中的this都指向了组件本身,但是为了接受客户端的响应而添加的回调函数,直接添加到了window对象上,再这个函数里面用到的this就指向了window而非React组件。

export default class PCIndex extends React.Component {

constructor(props) {

super(props);

this.state = {

updateCounter: 0,

}

};

async markPhoto(index) {

// ...

window.teacherMark = async function(result) {

// ...

this.setState({

updateCounter: (++this.state.updateCounter)

});

};

}

上面的setState会报错(可怕的是当时使用了Mobx,直接对组件内的属性赋值this.updateCounter++,没有报错而是直接无效)

解决方法有两个,一个是将组件的this缓存下来:

async markPhoto(index) {

const self = this;

// ...

window.teacherMark = async function(result) {

// ...

self.setState({

updateCounter: (++this.state.updateCounter)

});

};

}

第二种方法就是改用箭头函数:

async markPhoto(index) {

const self = this;

// ...

window.teacherMark = result => {

// ...

self.setState({

updateCounter: (++this.state.updateCounter)

});

};

}

对于this的指向,一定要谨慎!

循环的问题

其实这个问题和React关系不大,还是自己太菜。

有这样的一段代码,要求根据数组成员的某些属性筛选数后,创建一个新的数组,当时的做法啊是:

const questions = [

{ hasMarked: true},

{ hasMarked: false},

{ hasMarked: true}

];

let markParamAnswer = [];

questions.map((question, index) => {

if (question.hasMarked) {

markParamAnswer[index] = {

"answerId": "123",

"teacherName": "123",

"score": -1,

"comment": "111111"

};

}

});

console.log(markParamAnswer[1]); // undefined

console.log(markParamAnswer); // [{...}, empty, {...}]

但由于间隔着遍历导致数组,导致结果会形成带有空位的数组,在后面处理的时候出现了bug

当时的改进方案是:

const questions = [

{ hasMarked: true},

{ hasMarked: false},

{ hasMarked: true}

];

let markParamAnswer = [];

let i = 0;

questions.map((question, index) => {

if (question.hasMarked) {

markParamAnswer[index] = {

"answerId": "123",

"teacherName": "123",

"score": -1,

"comment": "111111"

};

i++

}

});

console.log(markParamAnswer); // [{...}, {...}]

但是现在来看,当时还是太菜,这就是没有code review的缘故,没人指出你的代码有多烂,只能靠自己回过头再看看,发现自己菜的一比。

可以直接用push就行了(2018-11-22):

questions.forEach((question, index) => {

if (question.hasMarked) {

markParamAnswer.push({

"answerId": "123",

"teacherName": "123",

"score": -1,

"comment": "111111"

});

}

});

如果数据量不大(因为会遍历两次)的时候可以写成函数式的,更清晰(2019-01-22):

let markParamAnswer = questions.filter(question => question.hasMarked).map(v => {

return {

"answerId": "123",

"teacherName": "123",

"score": -1,

"comment": "111111"

}

});

参考

  • React setState can only update a mounted or mounting [email protected]
  • react native Warning: setState(…): Can only update a mounted or mounting [email protected]
  • React Checkbox Stays Checked Even After Component is [email protected]
  • React 实践心得:key 属性的原理和用法

以上是 React提高01 遇到的坑 的全部内容, 来源链接: utcz.com/z/381856.html

回到顶部