上一节:高阶组件
下一节:深入JSX
React可以在任何web应用中使用。它可以与其他应用相互嵌套。本章将会介绍一些常用的嵌套的例子,主要侧重于jQuery和Backbone。但是使用这些的思想同样可以应用到其他整合案例中。
React无法感知到在React之外DOM改变。它根据内部虚拟DOM的改变来更新,但是如果DOM节点被第三方库更改了,那么React就会感到困惑并且没有办法恢复。
但这并不意味着将React和其他可以改变DOM节点的方式结合很难或者不可能,你只需要注意他们各自做了什么就可以了。
避免冲突最简单的方法就是阻止React组件更新。你可以渲染一个无需渲染的元素,比如空的 为了展示这个,我们来编写一个用于常用jQuery插件的wrapper。 我们将会将一个ref绑定在根DOM元素。在 为了防止React在DOM挂载之后触碰它,我们将会在 应该注意到了我们在这里声明了 为了给这些概念一个更具体的例子,我们来为Chosen插件编写一个简略的wrapper,它能需要输入一个 注意: 这只是一种方式,但不是最佳地使用React的方式。我们推荐尽可能地使用React组件。React组件在React应用中能够被更容易地复用,并且提供了更多的对自身行为和样式的控制。 首先,我们来看看Chosen对DOM节点做了什么。 如果你在 以下是我们最终要实现的代码结果: 为了简单起见,我们将以非受控组件来实现它。 首先,我们创建一个带有 注意在这里我们将 接下来,我们将会实现生命周期方法。我们需要在 注意React不会给 现在我们所作的已经能够让我们的组件被渲染了,但是我们还需要实时感知到值的修改。为了实现这个,我们需要订阅由Chosen管理的在 由于组件的props会随时间更改,所以我们不会将 现在,只剩下一件事了。在React中,props是会随时间变化的。举个例子,如果父组件的状态改变的话, Chosen的官方文档推荐我们使用jQuery 这样,当React管理的 完整的 由于React.render()的自由性,React可以在其他应用中嵌套使用。 尽管React通常被用作在启动时在DOM中加载单一的根组件,但是 事实上,这也是Facebook中使用React的方式。这让我们在应用中一小块一小块地使用React,并和已有的服务端生成的模板和其他客户端代码结合。 在老的web应用中一个常用的模式是将DOM语块作为字符串处理并将它插入到DOM节点中,例如 让我们来看下面通过jQuery实现的代码: 它可以被转化成React组件: 从这里开始,你可以把更多的逻辑代码编写到组件中并且开始应用更多的React实践。比如,在组件中最好不要使用ID,因为同一个组件可能会被渲染多次。对应地,我们将会使用React事件处理系统并且直接在React元素 只要你喜欢,你可以创建多个独立的这种组件。并且通过 Backbone通常使用HTML字符串或者返回字符串的模板函数来创建DOM元素的内容。这个过程也可以用渲染React组件来代替。 在下面的例子中,我们将会创建一个Backbone视图: {props.text} 在remove方法中调用ReactDOM.unmountComponentAtNode()是非常重要的,因为这样React就可以在解除的时候移除事件处理器以及组件树上其他资源的关联。 当一个组件从React组件树中移除时,清理工作是自动完成的,但是由于我们是手动地移除整个树,所以我们需要在手动地调用这个方法。 虽然通常推荐使用类似React state,Flux或Redux这类的单向数据流,React组件也可以使用其他框架和库的model层。 在React组件中使用Backbone的model和集合最简单的方法是监听不同的数据变化并且手动强制更新。 负责渲染model的组件将会监听 在下面的例子中, 上述方法要求你的React组件能够感知到Backbone的model和集合。如果你计划之后使用另外的数据管理方案,你可能会在代码中尽可能少的使用Backbone。 解决这个问题的一个方案是每当model的属性变化时将它提取成简单的数据,并且将它的逻辑保存在同一个地方。下面是一个高阶组件,它把所有的Backbone属性提取出来放入了state中,并将数据传递给被包裹组件。 通过这种方法,只有高阶组件需要知道Backbone的model内部实现,其他的大部分组件不需要知道这些。 在下面的例子中,我们将会从初始的state中复制一份model的属性。我们监听了 请注意这个例子不是集成Backbone的详尽的例子,但是它提供了一般集成时的思路: 为了解释如何使用这个,我们将名为
这项技术不仅限于Backbone。你可以在其他任何model库中使用,只要在生命周期方法中注册监听器就可以,当然,作为可选项,你也可以把数据复制到本地React的state中。 上一节:高阶组件如何解决这个问题
componentDidMount
方法中,我们将会获取到它的引用,这样我们就可以把它传递给jQuery插件了。render
方法中返回一个空class SomePlugin extends React.Component {
componentDidMount() {
this.$el = $(this.el);
this.$el.somePlugin();
}
componentWillUnmount() {
this.$el.somePlugin('destroy');
}
render() {
return
componentDidMount
和componentWillUnmount
两个生命周期函数。许多jQuery插件都在DOM元素上绑定了事件监听器,所以在componentWillUnmount
函数中为其解绑是很重要的。如果插件没有提供解绑的方法,那么你据需要自行编写一个方法来清除所有的监听器了。请记得移除所有插件注册的事件监听器以免内存泄漏。集成jQuery Chosen插件
参数。
DOM节点上调用了它,那么它会获取原始DOM节点上的属性并且用一个内联样式隐藏它。之后在
后面添加上它自身提供样式的DOM节点。在这之后它就激活了jQuery时间来通知我们DOM节点的更改。
function Example() {
return (
render
方法的空组件,在render方法中我们返回一个包裹了元素的
class Chosen extends React.Component {
render() {
return (
包裹在一个额外的
元素后面添加其他DOM元素。然而,就React而言,
componentDidMount
方法中初始化赋予节点的ref,并且在
componentWillUmount
中将其销毁:componentDidMount() {
this.$el = $(this.el);
this.$el.chosen();
}
componentWillUnmount() {
this.$el.chosen('destroy');
}
this.el
字段赋予特殊的含义。只有我们在render
方法中将一个ref
的值赋予给它时它才会有具体的作用:元素上的jQuery
change
事件。this.props.onChange
直接传递给Chosen,这其中也包含了事件处理权柄。对应的,我们将会声明一个handleChange()来调用this.props.onChange
,并且在jQuery的change
事件上注册它:componentDidMount() {
this.$el = $(this.el);
this.$el.chosen();
this.handleChange = this.handleChange.bind(this);
this.$el.on('change', this.handleChange);
}
componentWillUnmount() {
this.$el.off('change', this.handleChange);
this.$el.chosen('destroy');
}
handleChange(e) {
this.props.onChange(e.target.value);
}
组件可能会得到不同的子元素。这意味着从集成的角度来说,手动更新DOM以应对prop的更新是非常重要的,因为我们没有让React来替我们管理DOM。trigger()
API来通知对原始DOM元素的改变。我们将会让React来管理中的
this.props.children
的更新,同时我们也会在componentDidUpdate()
生命周期方法中通知Chosen关于子元素列表的改变:componentDidUpdate(prevProps) {
if (prevProps.children !== this.props.children) {
this.$el.trigger("chosen:updated");
}
}
的子元素更改时,Chosen就会去更新它的DOM元素。
Chosen
组件实现如下:class Chosen extends React.Component {
componentDidMount() {
this.$el = $(this.el);
this.$el.chosen();
this.handleChange = this.handleChange.bind(this);
this.$el.on('change', this.handleChange);
}
componentDidUpdate(prevProps) {
if (prevProps.children !== this.props.children) {
this.$el.trigger("chosen:updated");
}
}
componentWillUnmount() {
this.$el.off('change', this.handleChange);
this.$el.chosen('destroy');
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
return (
与其他视图库整合
React.render()
也可以在各自独立的UI中多次调用,比如一个按钮或者一整个应用。使用React替换基于字符串的渲染
$el.html(htmlString)
。代码库中的这些例子非常适合引入React。只需要将基于字符串的渲染重写成React组件即可。$('#container').html('');
$('#btn').click(function() {
alert('Hello!');
});
function Button() {
return ;
}
ReactDOM.render(
,
document.getElementById('container'),
function() {
$('#btn').click(function() {
alert('Hello!');
});
}
);
上注册事件处理器:
function Button(props) {
return ;
}
function HelloButton() {
function handleClick() {
alert('Hello!');
}
return ;
}
ReactDOM.render(
React.render()
把它们渲染到不同的DOM容器中。当你逐渐地将你的应用向React转变,你就可以将它们组合进更大的组件中,并且以此调整某些React.render()
所处的层级。把React嵌入到Backbone视图
ParagraphView
。它会重载Backbone的render()
函数来讲一个React组件
渲染到Backbone(this.el
)提供的DOM元素中。在这里,我们使用React.render()
:function Paragraph(props) {
return
和Model层集成
在React组件中使用Backbone的Model
change
事件,负责渲染集合的组件将会监听add
和remove
事件。在这两种情况中,都需要调用this.forceUpdate()来使用新数据重新渲染组件。List
组件渲染了Backbone的集合,使用Item
组件来渲染集合中的每一项。class Item extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.forceUpdate();
}
componentDidMount() {
this.props.model.on('change', this.handleChange);
}
componentWillUnmount() {
this.props.model.off('change', this.handleChange);
}
render() {
return
{this.props.collection.map(model => (
);
}
}
从Backbone的Model中提取数据
change
事件(在卸载时将监听器移除),当change事件发生时,我们就用当前的model属性更新state。最终,我们确定了当model
的prop改变时我们会为老的model解除监听并在新的model上设置监听器。function connectToBackboneModel(WrappedComponent) {
return class BackboneComponent extends React.Component {
constructor(props) {
super(props);
this.state = Object.assign({}, props.model.attributes);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.props.model.on('change', this.handleChange);
}
componentWillReceiveProps(nextProps) {
this.setState(Object.assign({}, nextProps.model.attributes));
if (nextProps.model !== this.props.model) {
this.props.model.off('change', this.handleChange);
nextProps.model.on('change', this.handleChange);
}
}
componentWillUnmount() {
this.props.model.off('change', this.handleChange);
}
handleChange(model) {
this.setState(model.changedAttributes());
}
render() {
const propsExceptModel = Object.assign({}, this.props);
delete propsExceptModel.model;
return
NameInput
的React组件与Backbone的model组合,并且在每次输入时更新它的firstName
属性:function NameInput(props) {
return (
My name is {props.firstName}.
下一节:深入JSX