在使用 React 开发项目过程中, 踩过很多坑, 在踩坑填坑过程中也学到了不少知识. 现就将之前遇到的问题总结下来, 一来可以让自己对这些知识点加深印象, 二来也可以帮助更多的人在遇到同样的问题时可以少走弯路.
组件名命名问题
类名首字母必须大写, 多个单词组合要用帕斯卡命名法 (首字母大写, 其它单词首字母也要大写)
state 更新问题
例如下面的例子中, 结果是 state 没更新; 因为 this.state.value 是对象类型数据, 和 value 指向的是同一个内存地址, 引用的是同一个数据; 对 value 的操作实际上就是操作原 this.state.value, 所以 value 和 this.state.value 相同, 对比下, 没有改变; 所以在这里的解决办法就是:
var value=this.state.value.slice(); 复制一个 this.state.value 出来给 value, 这样 value 改变了, 就可以跟 this.state.value 作对比了.
- getInitialState: function(){
- return {value: {foo: 'bar'}};
- },
- onClick: function(){
- var value = this.state.value; // 应该这样: var value = this.state.value.slice();
- value.foo += 'bar';
- this.setState({value: value});
- }
this 指向问题
- componentDidMount() {
- QueryStore.on('sync', this.handleQuerySync);
- this.initializeWithProps();
- }
this.handleQuerySync 的 this 并不是指向当前组件, 指向的是 QueryStore, 解决办法:(当然 es6 的箭头函数也可以解决 this 指向问题)
- ......
- constructor(props, context) {
- super(props, context);
- this.state = {};
- this.handleQuerySync = this.handleQuerySync.bind(this);
- }
- componentDidMount() {
- QueryStore.on('sync', this.handleQuerySync);
- this.initializeWithProps();
- }
- ......
render 返回格式
render 中 return 的内容要么是空, 要么就是一个节点, 所以当有很多节点需要 return 时, 需要用一个节点包裹起来.
js 对象中的 key 问题
看下面的代码, 函数中传入变量 key
- var BindingMixin = {
- handleChange: function(key){
- var that = this;
- return function(event){
- var newState = {};
- newState[key] = event.target.value;
- that.setState(newState);
- //that.setState({
- //key: event.target.value
- //})
- }
- }
- };
上面注释掉的代码是错误的, 因为 js 对象中的 key 值 不可以是变量
多次设置 state 问题
如下例子:
- this.setState({
- data: datas
- });
- this.setState({
- data: datas
- });
如果两次设置状态, datas 的值没有改变, 是不会触发 render 函数的; Javascrippt 是基于事件驱动模型, 假如在连续两次 this.setState() 之后, React 发现 DOM 没有变更, 此时 React 并不会触发 render 方法
react 组件开发时遇到的问题
- <Menu
- vertical={true}
- inverted={false}
- placement={false}>
- <Menu.Item title={<span>Home</span>} \>
- <Menu.Item title="Home List1" />
- <Menu.Item title="Home List2" />
- <Menu.Item title={<a href="">Home List3</a>} /\>
- </Menu.Item>
- <Menu.Item title={<span><a href="">List</a></span>} /\>
- </Menu>
如上面的代码, 如何把 Menu 的属性传到子节点组件 Menu.Item 中去呢? 解决方法是:
- renderList = (children, props) => {
- const extraProps = {
- inverted: props.inverted,
- vertical: props.vertical
- };
- return (
- React.Children.map(children, (child) => {
- return React.cloneElement(child, extraProps);
- })
- );
- };
- ......
外层组件渲染子节点组件时, 克隆子节点, 并把需要的属性传过去; 这样内层的子组件就可以拿到这些属性; 这里传过去的对象, 不限于属性, 方法也可以传过去;
内层组件如何复用外层组件的方法?
解决办法: 上面说了, 父组件渲染子组件时, 用克隆子组件的方法, 顺带把需要的属性传过去, 其实还可以把父组件的 this 对象传过去, 这样在子组件里, 就可以方便调用父组件的方法; 如下代码:
- // 外层父组件
- ......
- renderTreeNode = (child) => {
- const defaultExpandAll = this.props.defaultExpandAll;
- const extraProps = {
- root: this,
- defaultExpandAll: defaultExpandAll
- };
- return React.cloneElement(child, extraProps);
- };
- ......
- // 内层子组件这样调用
- ......
- newChildren = () => {
- let props = this.props;
- let children = this.props.children;
- return (
- <ul className={styles.subTree}>
- {React.Children.map(children, (item, index) => {
- return props.root.renderTreeNode(item, index);
- })}
- </ul>
- );
- };
- ......
- dangerouslySetInnerhtml
- var Test = React.createClass({
- getInitialState: function() {
- return {html: '<a href="#"> 这是一段 html 代码 </a><a href="#">2</a><a href="#">3</a>};
- },
- render: function() {
- return (
- <div>{this.state.html}</div>
- );
- }
- });
- React.render(<Test />, document.getElementById('example'));
解析出来的还是这样的一段代码
<a href="#"> 这是一段 html 代码 </a><a href="#">2</a><a href="#">3</a>'<br><br>
因为 react 不会自动帮你解析你的 html 代码, 不合时宜的使用 innerHTML 可能会导致 cross-site scripting (XSS) 攻击;
- var Test = React.createClass({
- getInitialState: function() {
- return {html: '<a href="#"> 这是一段 html 代码 </a><a href="#">2</a><a href="#">3</a>};
- },
- render: function() {
- return (
- <div dangerouslySetInnerHTML={{__html: this.state.html}}></div>
- );
- }
- });
- React.render(<Test />, document.getElementById('example'));
这么做的意义在于,{__html:...} 背后的目的是表明它会被当成 "type/taint" 类型处理. 这种包裹对象, 可以通过方法调用返回净化后的数据, 随后这种标记过的数据可以被传递给 dangerouslySetInnerHTML
其它一些遇到的问题
组件被卸载后, store 里的数据并不会被清空; 必要时得在组件挂着时清空 store 的数据
父组件监听数据变化就可以了, 不需要每个子组件自己又监听一遍, 这样容易出现 bug, 而且性能也不好, 可以通过 props 向子组件传递;
关于列表 key
遍历列表时给每个子项目赋 key 时, 不要直接给 index, 因为如果增加或者删除子项目时, index 都会变, 导致出问题; 也不可以用 index+ string, 这样因为如果增加或者删除子项目时, 所有子项目都会走更新流程, 原因是 index 在变; 所以在给子项目赋值时, 要给唯一不变的数最好.
关于 store 中的数据有变化, 但是组件中的监听函数没有监听到数据变化
这样的问题出现过很多次了, 不要钻牛角尖, 去郁闷为啥数据有变化监听不到呀, 为啥呀! 其实, 数据变化了, 监听不到, 本质原因就是没有监听; 可能其他的代码卸载了监听函数.
来源: https://juejin.im/post/5aead38c6fb9a07aab29b190