以前做项目主要用的是Vue,现在5月份加入React项目,记录平时项目中的踩坑点和接触到的注意事项。
1.遍历数组渲染dom需要设置key值,习惯使用数组索引做key,这样很容易出现bug。
key值应该使用唯一值,比如id等。
react监测差异时会根据key值来判断是否需要进行更新,当数组前几项被删除,后几项就会占用原来前几项的key值,key值没有发生改变,react不会对对应相同key值的dom做更新渲染。所以也不应该使用index做dom的key值。
2.state数据更新时如果依赖前一次的state,依照代码习惯有时候会使用如下代码
this.setState({
counter: this.state.counter + this.props.increment,
});
但是这样设置新的state,可能会出现数据不同步或者无法更新的情况。
应该使用setState()
接收一个函数而不是一个对象。
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
3.React 会将小写开头的标签名认为是 HTML 原生标签,所以组件命名必须以大写开头。
4.函数作为子代,props.children接受传参,可参考之后灵活使用props.chidren
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));
}
return <div>{items}</div>;
}
function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>This is item {index} in the list</div>}
</Repeat>
);
}
5.props.chidren设置多个入口
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
6.如果你想让类似 false
、true
、null
或 undefined
出现在输出中,你必须先把它转换成字符串 :
<div>
My JavaScript variable is {String(myVariable)}.
</div>
7.react项目打包构建后页面空白。
如果是路径找不到的问题,需要在package.json中增加配置homepage:'.'
如果修改上述配置后,页面仍然是空白,请检查你是否使用的是BrowserRouter而不是HashRouter(BrowserRouter需要后端配置支持)
8.(疑问)为什么react只有数组才需要key值,其他的元素不需要?答案:React 有能力辨别出,更新前后元素的对应关系
// Babel转换JSX前
const element = (
<div>
<h3>example</h3>
{[<p key={1}>hello</p>, <p key={2}>world</p>]}
</div>
);
// Babel转换后
"use strict";
var element = React.createElement(
"div",
null,
React.createElement("h3",null,"example"),
[
React.createElement("p",{ key: 1 },"hello"),
React.createElement("p",{ key: 2 },"world")
]
);
不管 props 如何变化,数组外的每个元素始终出现在 React.createElement() 参数列表中的固定位置,这个位置就是天然的 key
初学 React 时还容易产生另一个困惑,那就是为什么 JSX 不支持 if 表达式来有选择地输出(不能这样:{if(yes){ <div {...props}/> }}
),而必须采用三元运算符来完成这项工作(必须这样:{yes ? <div {...props}/>} : null
)。那是因为,React 需要一个 null 去占住那个元素本来的位置
*该知识点的发现,可以利用key值触发react组件的销毁和重建
比如用户相关组件,可以绑定key值为用户id代替react天然的key值,用户切换时key属性变化,组件相应也会销毁重建,这样可以避免在组件componentWillReceiveProps等生命周期中去做其他的一些代码逻辑来同步用户的切换
9.react性能优化----不要在 render 中重新定义函数
很多人喜欢在 render 函数中子组件通过构造一个箭头函数来传递给子组件,但是这样有一个问题就是,每次都会声明一个新的箭头函数,因而每次声明的函数都肯定是不同的,这样就无法阻止re-render
10.react的组件更新渲染
所以,合理拆分数据和组件,让未被修改地组件对改变无感知很重要。
11.在React中通过反模式获取props中函数的更新
// 1. App 会传递一个 prop 给 From 表单
// 2. Form 将向下传递一个函数给 button
// 这个函数与它从 App 得到的 prop 相接近
// 3. App 会在 mounting 之后 setState,并传递
// 一个**新**的 prop 给 Form
// 4. Form 传递一个新的函数给 Button,这个函数与
// 新的 prop 相接近
// 5. Button 会忽略新的函数, 并无法
// 更新点击处理程序,从而提交陈旧的数据
class App extends React.Component {
state = { val: "one" }
componentDidMount() {
this.setState({ val: "two" })
}
render() {
return <Form value={this.state.val} />
}
}
const Form = props => (
<Button
onClick={() => {
submit(props.value)
}}
/>
)
class Button extends React.Component {
shouldComponentUpdate() {
// 让我们假装比较了除函数以外的一切东西
return false
}
handleClick = () => this.props.onClick()
render() {
return (
<div>
<button onClick={this.props.onClick}>这个的数据是旧的</button>
<button onClick={() => this.props.onClick()}>这个工作正常</button>
<button onClick={this.handleClick}>这个也工作正常</button>
</div>
)
}
}
结果输出: 第一个‘one’,第二个第三个'two
第二次相比第一次,区别就是不是直接去执行 props.onClick,而是每次都包一个新的箭头函数,在每一次执行的时候都会去获取一个新的 this.props.onClick,这就是一切的关键,虽然 shouldComponentUpdate 为 false,但是新的 props 还是已经来了,可以通过 this.props 引用
启发:主动拉取更新的子组件来进行性能优化:像上例中的第二种和第三种方法,将子组件的 shouldComponentUpdate 返回 false,然后在传入的 props 的 handler 外面包一层匿名函数,这样每次调用 handler 都会去访问最新的 this.props.handler 等“非计划更新的 props”(函数的 props),这些函数的 props 可以返回父组件的一些内部状态传递给子组件。如此一来,子组件就从单向状态流变成了子组件向父组件主动拉取。但这与 React 的单向数据理念相左,是属奇技淫巧
12.React高阶组件
13.React.Children
借鉴:https://github.com/ybning/blog/issues/19
包含方法:
map,
forEach,
count,
toArray,
only,