微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Draft.js:仅在内容或样式更改时才触发onchange吗?

如何解决Draft.js:仅在内容或样式更改时才触发onchange吗?

我想在我的React项目中使用一个所见即所得的编辑器。

Draft.js看起来很棒。但是,我想在编辑器的内容或样式更改后3秒钟将编辑器的内容保存到import 'dart:isolate'; import 'dart:ui'; import 'dart:async'; import 'dart:io'; import 'package:Flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:Flutter_downloader/Flutter_downloader.dart'; import 'package:permission_handler/permission_handler.dart'; const debug = true; void main() async { WidgetsFlutterBinding.ensureInitialized(); await FlutterDownloader.initialize(debug: debug); runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final platform = Theme.of(context).platform; return new MaterialApp( title: 'Flutter Demo',theme: new ThemeData( primarySwatch: Colors.blue,),home: new MyHomePage( title: 'Downloader',platform: platform,); } } class MyHomePage extends StatefulWidget with WidgetsBindingObserver { final TargetPlatform platform; MyHomePage({Key key,this.title,this.platform}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { --------------------------------------------------------------------------------------- final _documents = [ { 'name': 'Learning Android Studio','link': 'http://barbra-coco.dyndns.org/student/learning_android_studio.pdf' },{ 'name': 'Android Programming Cookbook','link': 'http://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf' },{ 'name': 'iOS Programming Guide','link': 'http://darwinlogic.com/uploads/education/iOS_Programming_Guide.pdf' },{ 'name': 'Objective-C Programming (Pre-Course Workbook','link': 'https://www.bignerdranch.com/documents/objective-c-prereading-assignment.pdf' },]; final _images = [ { 'name': 'Arches National Park','link': 'https://upload.wikimedia.org/wikipedia/commons/6/60/The_Organ_at_Arches_National_Park_Utah_Corrected.jpg' },{ 'name': 'Canyonlands National Park','link': 'https://upload.wikimedia.org/wikipedia/commons/7/78/Canyonlands_National_Park%E2%80%A6Needles_area_%286294480744%29.jpg' },{ 'name': 'Death Valley National Park','link': 'https://upload.wikimedia.org/wikipedia/commons/b/b2/Sand_Dunes_in_Death_Valley_National_Park.jpg' },{ 'name': 'Gates of the Arctic National Park and Preserve','link': 'https://upload.wikimedia.org/wikipedia/commons/e/e4/GatesofArctic.jpg' } ]; final _videos = [ { 'name': 'Big Buck Bunny','link': 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' },{ 'name': 'Elephant Dream','link': 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4' } ]; ------------------------------------------------------------------------------------------- List<_TaskInfo> _tasks; List<_ItemHolder> _items; bool _isLoading; bool _permissionReady; String _localPath; ReceivePort _port = ReceivePort(); @override void initState() { super.initState(); _bindBackgroundisolate(); FlutterDownloader.registerCallback(downloadCallback); _isLoading = true; _permissionReady = false; _prepare(); } @override void dispose() { _unbindBackgroundisolate(); super.dispose(); } void _bindBackgroundisolate() { bool isSuccess = IsolateNameServer.registerPortWithName( _port.sendPort,'downloader_send_port'); if (!isSuccess) { _unbindBackgroundisolate(); _bindBackgroundisolate(); return; } _port.listen((dynamic data) { if (debug) { print('UI Isolate Callback: $data'); } String id = data[0]; DownloadTaskStatus status = data[1]; int progress = data[2]; final task = _tasks?.firstWhere((task) => task.taskId == id); if (task != null) { setState(() { task.status = status; task.progress = progress; }); } }); } void _unbindBackgroundisolate() { IsolateNameServer.removePortNameMapping('downloader_send_port'); } static void downloadCallback( String id,DownloadTaskStatus status,int progress) { if (debug) { print( 'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)'); } final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port'); send.send([id,status,progress]); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title),body: Builder( builder: (context) => _isLoading ? new Center( child: new CircularProgressIndicator(),) : _permissionReady ? _buildDownloadList() : _buildnopermissionWarning()),); } Widget _buildDownloadList() => Container( child: ListView( padding: const EdgeInsets.symmetric(vertical: 16.0),children: _items .map((item) => item.task == null ? _buildListSection(item.name) : DownloadItem( data: item,onItemClick: (task) { _openDownloadedFile(task).then((success) { if (!success) { Scaffold.of(context).showSnackBar(SnackBar( content: Text('Cannot open this file'))); } }); },onAtionClick: (task) { if (task.status == DownloadTaskStatus.undefined) { _requestDownload(task); } else if (task.status == DownloadTaskStatus.running) { _pauseDownload(task); } else if (task.status == DownloadTaskStatus.paused) { _resumeDownload(task); } else if (task.status == DownloadTaskStatus.complete) { _delete(task); } else if (task.status == DownloadTaskStatus.Failed) { _retryDownload(task); } },)) .toList(),); Widget _buildListSection(String title) => Container( padding: const EdgeInsets.symmetric(horizontal: 16.0,vertical: 8.0),child: Text( title,style: TextStyle( fontWeight: FontWeight.bold,color: Colors.blue,fontSize: 18.0),); Widget _buildnopermissionWarning() => Container( child: Center( child: Column( mainAxisSize: MainAxisSize.min,crossAxisAlignment: CrossAxisAlignment.center,children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0),child: Text( 'Please grant accessing storage permission to continue -_-',textAlign: TextAlign.center,style: TextStyle(color: Colors.blueGrey,SizedBox( height: 32.0,FlatButton( onpressed: () { _checkPermission().then((hasGranted) { setState(() { _permissionReady = hasGranted; }); }); },child: Text( 'Retry',style: TextStyle( color: Colors.blue,fontWeight: FontWeight.bold,fontSize: 20.0),)) ],); void _requestDownload(_TaskInfo task) async { task.taskId = await FlutterDownloader.enqueue( url: task.link,headers: {"auth": "test_for_sql_encoding"},savedDir: _localPath,showNotification: true,openFileFromNotification: true); } void _cancelDownload(_TaskInfo task) async { await FlutterDownloader.cancel(taskId: task.taskId); } void _pauseDownload(_TaskInfo task) async { await FlutterDownloader.pause(taskId: task.taskId); } void _resumeDownload(_TaskInfo task) async { String newTaskId = await FlutterDownloader.resume(taskId: task.taskId); task.taskId = newTaskId; } void _retryDownload(_TaskInfo task) async { String newTaskId = await FlutterDownloader.retry(taskId: task.taskId); task.taskId = newTaskId; } Future<bool> _openDownloadedFile(_TaskInfo task) { return FlutterDownloader.open(taskId: task.taskId); } void _delete(_TaskInfo task) async { await FlutterDownloader.remove( taskId: task.taskId,shouldDeleteContent: true); await _prepare(); setState(() {}); } Future<bool> _checkPermission() async { if (widget.platform == TargetPlatform.android) { final status = await Permission.storage.status; if (status != PermissionStatus.granted) { final result = await Permission.storage.request(); if (result == PermissionStatus.granted) { return true; } } else { return true; } } else { return true; } return false; } Future<Null> _prepare() async { final tasks = await FlutterDownloader.loadTasks(); int count = 0; _tasks = []; _items = []; _tasks.addAll(_documents.map((document) => _TaskInfo(name: document['name'],link: document['link']))); _items.add(_ItemHolder(name: 'Documents')); for (int i = count; i < _tasks.length; i++) { _items.add(_ItemHolder(name: _tasks[i].name,task: _tasks[i])); count++; } _tasks.addAll(_images .map((image) => _TaskInfo(name: image['name'],link: image['link']))); _items.add(_ItemHolder(name: 'Images')); for (int i = count; i < _tasks.length; i++) { _items.add(_ItemHolder(name: _tasks[i].name,task: _tasks[i])); count++; } _tasks.addAll(_videos .map((video) => _TaskInfo(name: video['name'],link: video['link']))); _items.add(_ItemHolder(name: 'Videos')); for (int i = count; i < _tasks.length; i++) { _items.add(_ItemHolder(name: _tasks[i].name,task: _tasks[i])); count++; } tasks?.forEach((task) { for (_TaskInfo info in _tasks) { if (info.link == task.url) { info.taskId = task.taskId; info.status = task.status; info.progress = task.progress; } } }); _permissionReady = await _checkPermission(); _localPath = (await _findLocalPath()) + Platform.pathSeparator + 'Download'; final savedDir = Directory(_localPath); bool hasExisted = await savedDir.exists(); if (!hasExisted) { savedDir.create(); } setState(() { _isLoading = false; }); } Future<String> _findLocalPath() async { final directory = widget.platform == TargetPlatform.android ? await getExternalStorageDirectory() : await getApplicationDocumentsDirectory(); return directory.path; } } class DownloadItem extends StatelessWidget { final _ItemHolder data; final Function(_TaskInfo) onItemClick; final Function(_TaskInfo) onAtionClick; DownloadItem({this.data,this.onItemClick,this.onAtionClick}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.only(left: 16.0,right: 8.0),child: InkWell( onTap: data.task.status == DownloadTaskStatus.complete ? () { onItemClick(data.task); } : null,child: Stack( children: <Widget>[ Container( width: double.infinity,height: 64.0,child: Row( crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[ Expanded( child: Text( data.name,maxLines: 1,softWrap: true,overflow: TextOverflow.ellipsis,Padding( padding: const EdgeInsets.only(left: 8.0),child: _buildActionForTask(data.task),],data.task.status == DownloadTaskStatus.running || data.task.status == DownloadTaskStatus.paused ? Positioned( left: 0.0,right: 0.0,bottom: 0.0,child: LinearProgressIndicator( value: data.task.progress / 100,) : Container() ].where((child) => child != null).toList(),); } Widget _buildActionForTask(_TaskInfo task) { if (task.status == DownloadTaskStatus.undefined) { return RawMaterialButton( onpressed: () { onAtionClick(task); },child: Icon(Icons.file_download),shape: CircleBorder(),constraints: BoxConstraints(minHeight: 32.0,minWidth: 32.0),); } else if (task.status == DownloadTaskStatus.running) { return RawMaterialButton( onpressed: () { onAtionClick(task); },child: Icon( Icons.pause,color: Colors.red,); } else if (task.status == DownloadTaskStatus.paused) { return RawMaterialButton( onpressed: () { onAtionClick(task); },child: Icon( Icons.play_arrow,color: Colors.green,); } else if (task.status == DownloadTaskStatus.complete) { return Row( mainAxisSize: MainAxisSize.min,mainAxisAlignment: MainAxisAlignment.end,children: [ Text( 'Ready',style: TextStyle(color: Colors.green),RawMaterialButton( onpressed: () { onAtionClick(task); },child: Icon( Icons.delete_forever,) ],); } else if (task.status == DownloadTaskStatus.canceled) { return Text('Canceled',style: TextStyle(color: Colors.red)); } else if (task.status == DownloadTaskStatus.Failed) { return Row( mainAxisSize: MainAxisSize.min,children: [ Text('Failed',style: TextStyle(color: Colors.red)),child: Icon( Icons.refresh,); } else { return null; } } } class _TaskInfo { final String name; final String link; String taskId; int progress = 0; DownloadTaskStatus status = DownloadTaskStatus.undefined; _TaskInfo({this.name,this.link}); } class _ItemHolder { final String name; final _TaskInfo task; _ItemHolder({this.name,this.task}); }

但是localStorage事件会被触发,即使光标或选择内容发生变化,或者聚焦或聚焦也不会触发。

任何仅由内容或样式更改触发的编辑器事件吗?还是可以解决

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。