Realm和React Native——实现自动更新的最佳实践?

17
3个回答

17

通过订阅事件并在接收到更改事件时更新UI,您可以使示例具有响应性。目前,仅在提交写事务时发送事件,但是更细粒度的更改事件将在将来添加。现在,您可以添加以下构造函数以在更改时更新UI:

constructor(props) {
  super(props);
  this.realm = new Realm({schema:[dogSchema]})
  this.realm.addListener('change', () => {
    this.forceUpdate()
  });
}
你需要持有一个 Realm 实例来保持通知的活性,并且你可以在组件的其余部分中使用这个 Realm 实例。
而不是调用 `forceUpdate`,你可以在事件监听器中设置组件的状态或属性来触发刷新,像这样:
constructor(props) {
  super(props);

  this.realm = new Realm({schema:[dogSchema]})

  this.state = {...}; // Initial state of component.

  this.realm.addListener('change', () => {
    this.setState({...}); // Update state instead of using this.forceUpdate()
  });
}

谢谢@Ari,我该如何将props设置到事件监听器中?用super(props)替换this.forceUpdate()吗? - ThorbenA
对于你的例子,你可能想要调用setState。而不是在render中计算dogProp,你应该将它放在state中,这将自动触发重新渲染。类似这样:this.setState({dogProp: this.realm.objects('Dog').length})。你还需要实现getInitialState - Ari
再次感谢 @Ari。有没有不引入本地组件状态的方法呢?(我正在使用Redux) - ThorbenA
2
如果您正在使用redux,我建议只使用Realm作为持久层,并在每次写入redux存储时写入Realm,使用redux在必要时触发UI呈现。未来我们希望有更好的集成。有关此主题的更多信息,请参见:https://github.com/realm/realm-js/issues/141 - Ari
1
在componentWillMount()中添加监听器不是更好吗?这样你就可以在componentWillUnmount()中移除监听器。 - Onno Faber
此解决方案有时会引发“警告:setState(...):只能更新已挂载或正在挂载的组件。这通常意味着您在未挂载的组件上调用了setState()”。 - yossi

9

我认为@Ari给了我一个很好的答案,适用于redux的人们,因为我也在苦苦挣扎。我不确定它是否足够不可变,但是它有效!

我只是在addListener内部调度getVehicles操作,它就可以正常工作!

Below is UI component whose constructor function makes the magic!

//- importing my realm schema
import realm from '../../db/models';
//- Importing my action
import { getVehicles } from './../../actions/vehicle';


@connect((store) => {
    return {
        vehicle: store.vehicle.vehicles
    }
})
export default class Devices extends Component {
    constructor(props) {
        super(props);

        realm.addListener('change', () => {
            props.dispatch(getVehicles());
        });

    }
} 

以下是在构造函数中使用的db/models文件。

import Realm from 'realm';

class VehicleSchema {};

VehicleSchema = {
    name: 'vehicleInfo',
    properties: {
        vehicleName: 'string',
        vehicleNumber: 'string',
        vehiclePassword: 'string',
        vehiclePasswordTrigger: 'bool',
        vehicleType: 'string',
        vehiclePicture: { type: 'data', optional: true }
    }
};

export default new Realm({schema: [VehicleSchema]});

Below is the actions/vehicle file, which gets dispatched in the constructor above.

import { queryVehicle } from './../db/queryVehicle';

export function getVehicles() {

    const vehicles = queryVehicle();

    return function(dispatch) {
        dispatch({type: "GOT_VEHICLES", payload: vehicles});
    }
}

以下是我的queryVehicle函数,用于在上述action文件中调用查询。

import vehicleModel from './models';

const queryVehicle = (queryInfo="vehicleInfo", filter='') => {

    const objects = vehicleModel.objects(queryInfo);

    if(filter.length === 0) return objects;

    let results = objects.filtered(filter);
    return results;


};

export { queryVehicle };

免责声明:我不确定这段代码是否足够不变,或者是否遵循良好的redux实践,因为我刚开始使用redux,请给我一些建议和评论,指出我的错误。

我还猜测在这里实现 reducer 并不重要。


1
最近遇到了Realm ListView自动更新的问题。当ListView行高度不同时,UI上可能会出现重叠的行。以下是我能够在不引起UI重叠的情况下重新渲染ListView的唯一方法。对我来说,这似乎有点“肮脏”,因此如果有更好的方法,我欢迎输入。但是到目前为止,这完美地工作着;以防其他人遇到此问题。

基本上,它只是清除dataSource,然后在有插入删除时再次使用setState回调插入它,但是修改只需滚动并自动更新即可。

let feed = this.props.store.feed;

feed.addListener((name, changes) => {
   if (changes.insertions.length || changes.deletions.length) {
       this.setState({dataSource: this.ds.cloneWithRows([])},
           () => this.setState({dataSource: this.ds.cloneWithRows(feed)})
       );
   } else {
        this.setState({dataSource: this.ds.cloneWithRows(feed)});
   }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接