React Tips

洪飞白
2023-12-01

一、JSX 中的 If-Else

来源

没法在JSX中使用 if-else 语句,因为 JSX 只是函数调用和对象创建的语法糖。

可以采用三元操作表达式

React.render(<div id={condition ? 'msg' : ''}>Hello World!</div>, mountNode);

当三元操作表达式不够健壮,你也可以使用 if 语句来决定应该渲染那个组件。

var loginButton;

if (loggedIn) {
    loginButton = <LogoutButton />;
} else {
    loginButton = <LoginButton />;
}

return (
  <nav>
    <Home />
    {loginButton}
  </nav>
)

二、自闭合标签

来源

JSX 中, <MyComponent /> 是合法的,而 <MyComponent> 就不合法。 所有的标签都必须闭合,可以是自闭和的形式,也可以是常规的闭合。

注意:
所有 React component 都可以采用自闭和的形式:<div />. <div></div> 它们是等价的。


三、JSX 根节点的最大数量

来源

目前, 一个 componentrender,只能返回一个节点。如果你需要返回一堆 div , 那你必须将你的组件用 一个divspan 或任何其他的组件包裹。

切记,JSX 会被编译成常规的 JS; 因此返回两个函数也就没什么意义了,同样地,千万不要在三元操作符中放入超过一个子节点。


四、Controlled Input 值为 null 的情况

来源

为每个controlled component指定 value 属性,来防止用户修改输入除非你希望如此。

也许会遇到这种问题:虽然已经指定了 value ,但是 input 依然可以未经允许就改变。这种情况,可能是因为一不小心将 value 设置成了 undefinednull

下面这条代码片段展示了这个现象,一秒钟之后,文本变得可编辑了。

React.render(<input value="hi" />, mountNode);

setTimeout(function() {
    React.render(<input value={null} />, mountNode);
}, 1000);

五、Mounting 后 componentWillReceiveProps 未被触发

来源

当节点初次被放入的时候 componentWillReceiveProps 并不会被触发。这是故意这么设计的。

原因是因为 componentWillReceiveProps 经常会处理一些和 old props 比较的逻辑,而且会在变化之前执行;不在组件即将渲染的时候触发,这也是这个方法设计的初衷。


六、getInitialState 里的 Props 是一个反模式

使用 props, 自父级向下级传递,在使用 getInitialState 生成 state 的时候,经常会导致重复的”来源信任”,i.e. 如果有可能,请尽量在使用的时候计算值,以此来确保不会出现同步延迟的问题和状态保持的问题。

糟糕的例子

var MessageBox = React.createClass({
  getInitialState: function() {
    return {nameWithQualifier: 'Mr. ' + this.props.name};
  },

  render: function() {
    return <div>{this.state.nameWithQualifier}</div>;
  }
});

React.render(<MessageBox name="Rogers"/>, mountNode);

更好的写法:

var MessageBox = React.createClass({
  render: function() {
    return <div>{'Mr. ' + this.props.name}</div>;
  }
});

React.render(<MessageBox name="Rogers"/>, mountNode);

对于更复杂的逻辑,最好通过方法将数据处理分离开来,然而,如果你理清了这些,那么它也就 不是 反模式了。两者兼得不是我们的目标:

var Counter = React.createClass({
  getInitialState: function() {
    // naming it initialX clearly indicates that the only purpose
    // of the passed down prop is to initialize something internally
    return {count: this.props.initialCount};
  },

  handleClick: function() {
      this.setState({count: this.state.count + 1});
  },

  render: function() {
      return <div onClick={this.handleClick}>{this.state.count}</div>;
  }
});

React.render(<Counter initialCount={7}/>, mountNode);

六、组件的 DOM 事件监听

来源

注意:
下面讲的是如何给 DOM 元素绑定 React 未提供的事件。 当你想和其他类库比如 jQuery 一起使用的时候,需要知道这些。

//Try to resize the window:

var Box = React.createClass({
  getInitialState: function() {
    return {windowWidth: window.innerWidth};
  },

  handleResize: function(e) {
    this.setState({windowWidth: window.innerWidth});
  },

  componentDidMount: function() {
    window.addEventListener('resize', this.handleResize);
  },

  componentWillUnmount: function() {
    window.removeEventListener('resize', this.handleResize);
  },

  render: function() {
    return <div>Current window width: {this.state.windowWidth}</div>;
  }
});

React.render(<Box />, mountNode);

componentDidMount 会在 component 渲染完成且已经有了 DOM 结构的时候被调用。通常情况下,你可以在这绑定普通的 DOM 事件。

注意,事件的回调被绑定在了 react 组件上,而不是原始的元素上。React 通过一个 autobinding 过程自动将方法绑定到当前的组件实例上。


七、JSX 的 false 处理

来源

下面展示了在不同上下文中 false 的渲染:

a、被渲染成 *id=”false”:*

React.render(<div id={false} />, mountNode);

b、String “false” as input value:

input value 的值将会是 “false” 字符串

React.render(<input value={false} />, mountNode);

c、没有子节点

React.render(<div>{false}</div>, mountNode);

上面这个没有被渲染成 false 字符串是因为要考虑到这种常见的情况:<div>{x > 1 && 'You have more than one item'}</div>.

//Demo

var App = React.createClass({
    componentDidMount: function() {
        // This doesn't refer to the `span`s! It refers to the children between
        // last line's `<App></App>`, which are undefined.
        console.log(this.props.children);
    },

    render: function() {
        var x = 2;
        return <div id={false}><span>{false}</span> <span>{x>1 && 'You have more than one item'}</span> <input type="text" value={false} /></div>;
    }
});

//生成标签代码
<div id="demo5">
    <div id="false" data-reactid=".3">
        <span data-reactid=".3.0"></span>
        <span data-reactid=".3.1"> </span>
        <span data-reactid=".3.2">You have more than one item</span>
        <span data-reactid=".3.3"> </span>
        <input type="text" value="false" data-reactid=".3.4">
    </div>
</div>

八、组件间的通信

来源

对于 父-子 通信,直接 pass props.

对于 子-父 通信: 例如: GroceryList 组件有一些通过数组生成的子节点。当这些节点被点击的时候,你想要展示这个节点的名字:

var GroceryList = React.createClass({
  handleClick: function(i) {
    console.log('You clicked: ' + this.props.items[i]);
  },

  render: function() {
    return (
      <div>
        {this.props.items.map(function(item, i) {
          return (
            <div onClick={this.handleClick.bind(this, i)} key={i}>{item}</div>
          );
        }, this)}
      </div>
    );
  }
});

React.render(
  <GroceryList items={['Apple', 'Banana', 'Cranberry']} />, mountNode
);

注意 bind(this, arg1, arg2, ...) 的使用: 我们通过它向 handleClick 传递参数。 这不是 React 的新概念,而是 JavaScript 的。

对于没有 父-子 关系的组件间的通信,你可以设置你自己的全局事件系统。 在 componentDidMount() 里订阅事件,在 componentWillUnmount() 里退订,然后在事件回调里调用 setState()


九、组件的引用

来源

如果你正在一个大型的非 React 应用里使用 React 组件,或者准备将你的代码转换成 React,你可能需要保持组件的引用。 React.render 会返回一个渲染后的组件的引用:

var myComponent = React.render(<MyComponent />, myContainer);

记住,JSX 并不会返回组件的引用! 它只是一个 ReactElement : 一个用来告知 React 渲染后的组件应该长什么样子的轻便的标识符。

var myComponentElement = <MyComponent />; // 只是 ReactElement.

// Some code here...

var myComponentInstance = React.render(myComponentElement, myContainer);

注意:
这中引用只能在最顶层级使用。 在组件内部,让 propsstate 来处理组件间的通信,而且只能通过 refs. 来引用。


十、this.props.children undefined

来源

你没办法通过 this.props.children 取得当前组件的子元素。 因为this.props.children 返回的是组件拥有者传递给你的 passed onto you 子节点。

var App = React.createClass({
  componentDidMount: function() {
    // This doesn't refer to the `span`s! It refers to the children between
    // last line's `<App></App>`, which are undefined.
    console.log(this.props.children);
  },

  render: function() {
    return <div><span/><span/></div>;
  }
});

React.render(<App></App>, mountNode);

如果想看更多地例子, 可以参考在 front page 里最后一个例子。

十一、与其他类库并行使用React

来源

你不用非得全部采用 React。组件的 生命周期事件,特别是componentDidMountcomponentDidUpdate,非常适合放置其他类库的逻辑代码。

var App = React.createClass({
  getInitialState: function() {
    return {myModel: new myBackboneModel({items: [1, 2, 3]})};
  },

  componentDidMount: function() {
    $(this.refs.placeholder.getDOMNode()).append($('<span />'));
  },

  componentWillUnmount: function() {
    // Clean up work here.
  },

  shouldComponentUpdate: function() {
    // Let's just never update this component again.
    return false;
  },

  render: function() {
    return <div ref="placeholder"/>;
  }
});

React.render(<App />, mountNode);

你还可以通过这种方式来绑定你自己的 事件监听(event listeners) 甚至是 事件流(event streams)。

十二、Dangerously Set innerHTML

来源

var FormClass5 = React.createClass({
    render: function () {
        var style = {marginTop: 10};

        function createMarkup(){
            return {__html: "First & middot; Second"}
        }
        return (
            <div>
                <div dangerouslySetInnerHTML={createMarkup()}></div>

                <select multiple={true} value={['B','C']} style={style}>
                    <option value="A">Apple</option>
                    <option value="B">Banana</option>
                    <option value="C">Cranberry</option>
                </select>
            </div>
        );
    }
});
React.render(
    <FormClass5 />,
    document.getElementById('demo5')
);

生成html:

<div id="demo5">
    <div data-reactid=".4">
        <div data-reactid=".4.0">First &amp; middot; Second</div>
        <select multiple="" style="margin-top:10px;" data-reactid=".4.1">
            <option value="A" data-reactid=".4.1.0">Apple</option>
            <option value="B" data-reactid=".4.1.1">Banana</option>
            <option value="C" data-reactid=".4.1.2">Cranberry</option>
        </select>
    </div>
</div>
function createMarkup() { return {__html: 'First &middot; Second'}; };
<div dangerouslySetInnerHTML={createMarkup()} />

这么做的意义在于,当你不是有意地使用 <div dangerouslySetInnerHTML={getUsername()} /> 时候,它并不会被渲染,因为 getUsername() 返回的格式是 字符串 而不是一个 {__html: ”} 对象。{__html:…} 背后的目的是表明它会被当成 “type/taint” 类型处理。 这种包裹对象,可以通过方法调用返回净化后的数据,随后这种标记过的数据可以被传递给 dangerouslySetInnerHTML。 基于这种原因,我们不推荐写这种形式的代码:<div dangerouslySetInnerHTML={{__html: getMarkup()}} />.

这个功能主要被用来与 DOM 字符串操作类库一起使用,所以提供的 HTML 必须要格式清晰(例如:传递 XML 校验 )

 类似资料:

相关阅读

相关文章

相关问答