-
Notifications
You must be signed in to change notification settings - Fork 926
Best way to run JavaScript on the server before some components #142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is an interesting request. I could add a callback that's called when a JS engine is initialised, somewhere around here: https://github.com/reactjs/React.NET/blob/master/src/React.Core/JavaScriptEngineFactory.cs#L106. That would run for every request though, which you don't want to do. I think the issue you'll face is that JavaScript engines are actually shared/pooled between multiple requests, so you shouldn't really have any singletons. This is the same with pretty much any server-side JavaScript environment (such as ReactJS.NET but also with Node.js). You could work around that by turning off pooling, which wouldl force it to create a brand new engine for every request. This is a bit slower. The other approach would be to have a unique ID per request and store all data for that particular request in an object keyed by that ID (eg.
You can do exactly that if you want to test it out. You can get the React environment via:
This has an execute method to run arbitrary JavaScript. Like I mentioned earlier, ensure you turn off pooling if you have any global per-request data. |
@Daniel15 don't you load an engine from the pool per request lifecycle? So the same engine gets used through out the life span of a request. I believe so. If thats the case then you could inject a global variable into the engine using the Environment.Execute mentioned above, then do all your React goodness in your templates and then finally clean up the variable before the request finishes by Executing a delete statement with the var name: delete myStore; If you set global vars you HAVE to clear them out manually at the end of each request or they gobble up memory real quick as it seems they won't get garbage collected otherwise. What is your store doing that needs initialising? Could it not be lazy loaded when the first component hits it? |
Oh good point, that would work. I think I'll add support for running some code before and after engines are obtained from the pool, it could be useful.
This might work too. |
Really would be great if somehow the React Router could be used in conjunction with React.NET. |
I've been away on holiday but I'm hoping to have a play around with React.Net again and see if we can't port over some of the bits we've learnt from SuperChargedReact as promised. First up would be pulling the start up code into a external file (or at least giving you the option to) so you can run React.Router or just render a component. |
What is the correct way to set a global variable in the engine? I have been using this: React.ReactEnvironment.Current
.Execute("var myStore = Components.StoreFactory({ hello: 'world' });"); But find that after rendering a few components the variable is no longer available from the global scope. |
@Jarlotee - It's tricky, since the JavaScript engines are pooled and reused across instances. You could turn off pooling in the config ( In general I haven't really thought of a good way of doing this, but I'm open to ideas! |
Ah just saw #270 where this change was made, looks like a work around for garbage collections. |
Best bet is to always add it in every run if you want to be sure its there. This can be quite speedy if you use a compiled script (as in ClearScript compiled scripts which parse the script once then save it for reuse to save the parse step next time) but that takes some tinkering to get working in React.Net due to the JSPool stuff. Could add it into the ClearScript code though but you might have to add to the Interfaces etc. for all the engines. |
Also if you add it to your main app.js (or whatever your start page is in your bundle, assuming you are webpacking it up or similar?) then you can use this to force it to be put into global if using "require" (ala node modules) or "import" if you are going the ES6/Babel route: global.myStore = new MegaStore(); |
Hi all, I have a similar issue with server side rendering using React.Core and pooling enabled, when I wanted to pass a .Net service as a function to a component. I noticed that I can't submit a .Net model object's method, a delegate nor a Func<> to environment.CreateComponent(), most probably because only it's JSON serialized form is passed to RenderHtml(). I think this is by design and that it is OK. So, just to evaluate, I looked up the source code, hacked a handle to a IJsEngine object and injected a .Net object directly with engine.EmbedHostObject("myservice", new MyService());. Now I can indeed use myservice-methods as I wanted, single-threaded. So there are no principal obstacles using .Net methods. Now here is the problem: since RenderHtml() indirectly uses the environment's Engine property, you cannot be sure which engine from a pool is used to render the component and thus if the .net service targets the desired component. My request is: Please add a way to make full .Net objects including their methods accessible to the rendering process. How? I don't know. Probably by adding two optional parameters to RenderHtml(..., string globalObjectName, object/dynamic globalObject), or by providing a public way to allocate/deallocate an engine and making RenderHtml(IReactComponent cmp, bool renderContainerOnly = false, bool renderServerOnly = false) a method of IJsEngine. I'm not quite sure, if a callback that's called when a JS engine is initialised, would be a good way, it would be "too global" imho. PS: I'm fully aware that such code won't work client side any more in the first place, unless such global service variables can be provided as JS code on the client. |
Evaluating arbitrary Javascript at runtime is possible now by implementing See how we're implementing CSS-in-JS support as an example: https://github.com/reactjs/React.NET/blob/master/src/React.Core/RenderFunctions/StyledComponentsFunctions.cs |
I have a scenario where I have a few independent components on a page accessing data from a "central store". The central store needs to be initialized before the components first render.
My first solution was (before introducing server side rendering), to simply call
store.initialize()
beforeReact.render()
Now I would need to somehow call
store.initialize()
on the server before calling@Html.React()
in the same JS context.I could call
ReactSiteConfiguration.Configuration.AddScript()
on a file that initializes the store, but that runs on every page of my application.I want to initialize the store on specific pages only.
I would somehow need to get the JavaScript engine instance that React.NET uses from
JsEngineSwitcher
and callExecute("store.initialize();")
on it.Can I do that?
The text was updated successfully, but these errors were encountered: