Ace editor. Set current edits state as base - javascript

I'm using ace editor wrapped by react-ace for my project.
How can I destroy all undo/redo history in certain moment when I need it?
I've tried this.aceRef.editor.destroy(); (just react ref bound with editor), but unfortunately it doesn't play well in some circumstances.
So, I just need to clean undo/redo session/history or something like that.
Any solution?

Ace editor actually has this reset method with UndoManager.
Then you need to assign temporary stored undomanager as base.
It could be done smth like that:
const { editor } = this.aceRef;
const session = editor.getSession();
const undoManager = session.getUndoManager();
undoManager.reset();
session.setUndoManager(undoManager);

Related

Populate 3rd party element from Redux with JSON instead of innerHTML or attributes

I am using a 3rd party library to generate a sortable grid that expects to be fed JSON data, is there a way to send part of the redux store to this non-react grid other than as a react component, attribute or innerHtml since it does expect JSON? The challenges are that all my redux scripts are modules which is a scoping issue since, in this case, the 3rd party element is wrapped in asp.net so I can't expose it to a function (though it can call a javascript function to get the data), and that I need to be able to subscribe to changes, this has proven unnecessarily difficult so far.
You can just call store.getState() to get the current state of the store.
You may want to attach the store on to the window to give it global scope when you create it,
window.redux = { store }
or if using modern js, create a wrapper export function in the file that you create it
export const getStore = () => store
So that you can then do
getStore().getState()
If I understand your question correctly, you are using some third-party element in a react app that takes data in the form of JSON? Without more details on the third-party library its hard to give a proper answer but I'll give it a shot.
Whatever this library is, you have to render it somehow at some point. Since we don't know what you are using I am going to pick jQuery.
Assume the following usage
$("#grid").initializeGrid(data);
You can expect the react component to be something like
const GridWrapper = (props) => {
const tableData = useSelector(selectTableJson);
useEffect(() => {
$("#grid").initializeGrid(data);
}, [tableData]);
return (
<div id="grid" />
);
}
Basically what is happening is you select the data from the redux store, and then initialize the third party component with that data.

Having trouble with persisting React state using localStorage with a SSR (Next.js) app

The app I'm making has customizable settings. I'd like to load default settings, then when a user makes any changes, the custom settings will be stored in localStorage. Now the next time a user comes back, we'll load the setting from their localStorage.
I'm using React context like so...
const SettingsContextProvider = (props: any) => {
const [settings, setSettings] = useState(getSettings());
useEffect(() => {
localStorage.setItem('settings', JSON.stringify(settings))
}, [settings]);
return (...some jsx...);
}
Then getSettings()...
getSettings() {
// get from local storage
if (process.browser) {
const localSettings = localStorage.getItem('settings');
if (localSettings) return JSON.parse(localSettings );
}
// fall back to default settings
return "Something else";
}
The issue I'm having is that the server side load (on the node side), we don't have local storage (which is why I check for process.browser), so it falls back to default settings. THEN when it gets to the browser, it seems to call getSettings() again, in which case we DO have local storage and it loads from there.
That works, but then I get an error:
Text content did not match. Server: "Something else" Client: "Something custom"
So I understand that the server isn't matching the client and it's upset about it. I get WHY it's happening, but I have NO IDEA how to fix it. Maybe I need to implement reducers or use cookies?
Has anyone come across this?
I'm more than happy to share my full repo code if it'll help.
I'm fairly comfortable with react but have pretty much NEVER used react hooks in my own code.
However, I feel like I wouldn't put the updating of the localStorage into a useEffect function like that (and I could be totally wrong about that).
So, first, I would check that that useEffect function is being called when you're expecting it to be.
And then, I would write a function
const updateSettings = (newSettings) => {
localStorage.setItem('settings', JSON.stringify(newSettings))
setSettings(newSettings);
}
And use that updateSettings function to pass down to components.
BUT, I could be totally off there, like I said I don't use react hooks and only have a theoretical understanding.

Write Vue plugin with custom options

I'm trying to write a vue plugin with custom options. I followed the official vue guidelines (https://v2.vuejs.org/v2/guide/plugins.html) on doing so but can't find a way to define custom options. These options should be read by normal javascript which then exports an object that is used by a vue component.
My folder structure is like this:
/src
factory.js
CustomComponent.vue
factory.js
import Vue from "vue";
import ImgixClient from "imgix-core-js";
var imgixClient = new ImgixClient({
domain: CUSTOM_OPTION_URL <-- important bit
domain: Vue.prototype.$imgixBaseUrl //tried it like this
});
export { imgixClient };
I already tried to set this custom bit by utilizing Vue.prototype in the install method like this but can't seem to get it working
export function install(Vue, options) {
if (install.installed) return;
install.installed = true;
Vue.prototype.$imgixBaseUrl = options.baseUrl;
Vue.component("CustomComponent", component);
}
I'm afraid this isn't going to be the simple answer you might have been hoping for... there's a lot to unpick here.
Let's start with factory.js. That is not a factory. It's a singleton. Singletons have problems around dependencies, configuration and the timing of instantiation and that's precisely the problem you're hitting. More on that later.
Now let's take a look at the plugin. First up, these two lines:
if (install.installed) return;
install.installed = true;
That shouldn't be necessary. Vue already does this automatically and should ensure your plugin is only installed once. Perhaps this came from an old tutorial? Take a look at the source code for Vue.use, there's not a lot to it:
https://github.com/vuejs/vue/blob/4821149b8bbd4650b1d9c9c3cfbb539ac1e24589/src/core/global-api/use.js
Digging into the Vue source code is a really good habit to get into. Sometimes it will melt your mind but there are some bits, like this, that aren't particularly difficult to follow. Once you get used to it even the more opaque sections start to become a little clearer.
Back to the the plugin.
Vue.prototype.$imgixBaseUrl = options.baseUrl;
It is not clear why you are adding this to the prototype.
I'm going to assume you are already familiar with how JavaScript function prototypes work.
Component instances are actually instances of Vue. So any properties added to Vue.prototype will be inherited by your components with almost no overhead. Consider the following simple component:
<template>
<div #click="onClick">
{{ $imgixBaseUrl }}
</div>
</template>
<script>
export default {
methods: {
onClick () {
const url = this.$imgixBaseUrl
// ...
}
}
}
</script>
As $imgixBaseUrl is an inherited property it is available within onClick via this.$imgixBaseUrl. Further, templates resolve identifiers as properties of the current Vue instance, so {{ $imgixBaseUrl }} will also access this.$imgixBaseUrl.
However, if you don't need to access $imgixBaseUrl within a component then there is no need to put it on the Vue prototype. You might as well just dump it directly on Vue:
Vue.imgixBaseUrl = options.baseUrl;
In the code above I've ditched the $ as there's no longer a risk of colliding with component instance properties, which is what motivates the $ when using the prototype.
So, back to the core problem.
As I've already mentioned, singletons have major problems around creation timing and configuration. Vue has its own built-in solution for these 'do it once at the start' scenarios. That's what plugins are. However, the key feature is that plugins don't do anything until you call install, allowing you to control the timing.
The problem with your original code is that the contents of factory.js will run as soon as the file is imported. That will be before your plugin is installed, so Vue.prototype.$imgixBaseUrl won't have been set yet. The ImgixClient instance will be created immediately. It won't wait until something tries to use it. When Vue.prototype.$imgixBaseUrl is subsequently set to the correct value that won't have any effect, it's too late.
One way (though not necessarily the best way) to fix this would be to lazily instantiate ImgixClient. That might look something like this:
import Vue from "vue";
import ImgixClient from "imgix-core-js";
var imgixClient = null;
export function getClient () {
if (!imgixClient) {
imgixClient = new ImgixClient({
domain: Vue.prototype.$imgixBaseUrl
});
}
return imgixClient;
}
So long as nothing calls getClient() before the plugin is installed this should work. However, that's a big condition. It'd be easy to make the mistake of calling it too soon. Besides the temporal coupling that this creates there's also the much more direct coupling created by sharing the configuration via Vue. While the idea of having the ImgixClient instantiation code in its own little file makes perfect sense it only really stands up to scrutiny if it is independent of Vue.
Instead I'd probably just move the instantiation to within the plugin, something like this:
import ImgixClient from "imgix-core-js";
export default {
install (Vue, options) {
Vue.imgixClient = Vue.prototype.$imgixClient = new ImgixClient({
domain: options.baseUrl
});
Vue.component("CustomComponent", component);
}
}
I've made a few superficial changes, using a default export and wrapping the function in an object, but you can ignore those if you prefer the way you had it in the original code.
If the client is needed within a component it can be accessed via the property $imgixClient, inherited from the prototype. For any other code that needs access to the client it can either be passed from the component or (more likely) grabbed directly from Vue.imgixClient. If either of these use cases doesn't apply then you can remove the relevant section of the plugin.

Draft.js and stateToHTML, how to render output html in react component?

I've got my Draft js editor working fine, it saves to my database, using convertToRaw(editorState1.getCurrentContent()), and I am getting it back and converting it to HTML using draft-js-export-html and stateToHTML(convertFromRaw(dbContent.content.text01)).
So far so good... but now I have raw HTML that I want to display in my react component, and here comes the trouble.
Just rendering it with {props.text01} outputs it as a string and turns into <p>adfasfadfs</p> as text.
Obviously I want it to render as HTML. How do I do that?
I have looked at dangerouslySetInnerHTML but I would prefer not having ANOTHER plugin just to get my Draft js working the way I want it to.
Am I going at it the wrong way, or is dangerouslySetInnerHTML the only way to do it?
As described in this answer, React will escape HTML by default (that's why it's just rendered as a string). But you can force the rendering by using the dangerouslySetInnerHTML property. You'd just have to take potential XSS attacks into consideration.
If Draft is already installed, another way to just simply render the content is to use the readOnly prop on the DraftEditor.
you can use this npm module.
link is link
npm install --save html-to-react
Examples
Simple
The following example parses each node and its attributes and returns a tree of React elements.
var ReactDOMServer = require('react-dom/server');
var HtmlToReactParser = require('html-to-react').Parser;
var htmlInput = '<div><h1>Title</h1><p>A paragraph</p></div>';
var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlInput);
var reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);
assert.equal(reactHtml, htmlInput); // true

Pass props down to scenes and modify

Building an app using React Native and the individual scenes are working great, really happy with it.
However now I'm looking at introducing some "global" state, so for instance to check if a user has an active session and if so add their data to some state which is shared (or passed) to the rest of the app.
I'm just not 100% where to put this code and where I'd store this data so the individual scenes can access (and also just as importantly - modify) this data?
I've had a stab at adding a prop to the <Navigator />, like so:
<Navigator user={this.getUser()} />
Which is then passed as a prop to <SomeScene navigator={navigator} /> but this doesn't feel right, and I can't figure out how to then modify a prop of navigator (say a user property changes) then have these changes filter back up to the main <Navigator />.
From searching around I've seen Flux and React Router mentioned, but can't find an example of what I'm trying to do so was hoping someone could point me in the right direction.
Thanks in advance.
This can be achieve using Flux, let me explain me with Flux, Flux is pattern which have Store and you can use the Store to store the Variable value here I am cover your example you want to store user information and want to access on each and every component for that your store contains two method one is for set User information and second one is for get the user information.
First you have to install the two module for this using below mentioned command
npm install events underscore --save
var EventEmitter = require('events').EventEmitter;
var _ = require('underscore');
var _user;
// Extend UserStore with EventEmitter to add eventing capabilities
var UserStore = _.extend({}, EventEmitter.prototype, {
getUserInfo: function() {
return _user;
},
setUserInfo: function(user) {
_user=user;
}
});
module.exports=UserStore;
now you can use it this Store from any of your component like
var UserStore=require('./UserStore');
and set and get the userinfor from anywhere in your application like to set
UserStore.setUserInfo(user);
var user=UserStore.getUserInfo();

Categories

Resources