Closed kpyfan closed 4 years ago
I think what you want can be done with the Piral instance.
Either you use the "root" pilet, e.g.,
const instance = createInstance({ ... });
instance.root.setData('foo', 'bar');
or you just use the action, e.g.,
const instance = createInstance({ ... });
instance.context.writeDataItem('foo', 'bar');
The latter also allows you to set expiration and owner directly and will always write. The former behaves exactly as if a pilet would write.
(The examples only show setData
, but getData
works the same)
Alternatively, you can also just initialize the state properly.
const instance = createInstance({
state: {
data: {
foo: {
value: 'bar',
owner: '',
target: 'memory',
expires: -1,
},
},
},
});
However, the best way to share functions (or information) is to provide an API from the Piral instance.
function createCustomApi() {
// return a constructor using the global context
return context => {
// return a constructor for each local API using the pilet's metadata
return (api, meta) => ({
foo: 'bar', // this is the API to return; just a "static" foo - but you could have functions, etc.
});
};
}
const instance = createInstance({
extendApi: [createCustomApi()],
});
If its really about the render
part and you'd not like to change the data initially, you can also go ahead as such
const MyComponentInPiral = () => {
const setData = useAction('writeDataItem');
setData ('foo', 'bar');
return <div>Render something</div>;
};
Is that what you want?
Documentation update landed in develop
.
Still - if you have some other need here that was not covered in the documentation (or in Piral at all) let us know!
Yeah, this looks like roughly what I want. Right now I'm loading in the data the componentDidMount
function on my menu component, but that's easy enough to move.
At the moment, my index.tsx
looks like this:
import * as React from 'react';
import {renderInstance, setupGqlClient} from 'piral';
import {layout, errors} from './layout';
const axios = require('axios');
renderInstance({
settings: {
gql: setupGqlClient({
subscriptionUrl: false,
}),
},
requestPilets() {
let promise = axios.get('/service/v1/pilets');
let items = promise.then(res => res.data)
.then(res => res.items);
return items;
},
layout: layout,
errors: errors,
});
export * from 'piral';
Since renderInstance
calls createInstance
and returns the instance itself, I can just use the return value of renderInstance
instead here and set my data. What would be the best way to get this info into my component then? Do we have the ability to get the current piral instance api as a part of one of my components? Or can I pass it down through my layout somehow?
Yeah so this file looks pre-Piral v0.9.
So first I'd like to show the migration path (then I'll discuss what you can without migration).
After migration your file would look:
import * as React from 'react';
import {renderInstance} from 'piral';
import {layout, errors} from './layout';
const axios = require('axios');
renderInstance({
requestPilets() {
let promise = axios.get('/service/v1/pilets');
let items = promise.then(res => res.data).then(res => res.items);
return items;
},
layout,
errors,
});
(I don't think you use GraphQL - so happy news for you: piral-urql
is not opt-in and thus there is no need to disable subscriptions; they are not available when you did not add and set up piral-urql
explicitly)
Also notice that the export is gone.
Alright, so far so good - but how does that help us regarding an instance
? Well, renderInstance
gets a configuration to call createInstance
under the hood. Actually, it calls createPiral
, see:
const instance = createPiral(config, settings);
createPiral
is just a wrapper to call createInstance
with the API extensions from piral-ext
. So how does the whole story help you? Guess what... renderInstance
also returns the created instance
.
So instead of
const instance = createInstance({ ... });
instance.root.setData('foo', 'bar');
you can also do
const instance = renderInstance({ ... });
instance.root.setData('foo', 'bar');
It's the same. I think you also had it that far:
Since renderInstance calls createInstance and returns the instance itself, I can just use the return value of renderInstance instead here and set my data. What would be the best way to get this info into my component then? Do we have the ability to get the current piral instance api as a part of one of my components? Or can I pass it down through my layout somehow?
There are two options, but I would just go with the one I outlined above (so no need to pass down anything):
const MyComponentInPiral = () => {
const setData = useAction('writeDataItem');
setData ('foo', 'bar');
return <div>Render something</div>;
};
Would that work?
Hope that helps!
Thanks for the help! I'll give it a shot and see if I run into any other issues.
Alright, so I've run into one minor issue here - on a deployed instance where latency is higher, we've got a race condition with the way I'm doing things. I wanted to pick your brain on the correct way to handle this situation.
In the index.js for the Piral instance, we're making an XHR call using Axios. In the promise, we're calling instance.root.setData('foo', 'bar');
to store the result of the XHR call as data that the pilets can access.
As a part of the base layout (in the menu), we're passing in the instance
above to the menu as a prop. What we want to do here is set the state in the menu using the data from the XHR call above. However, sometimes the component renders before the XHR call has returned, resulting in no data being available. As best I can tell, the better way to do this would be to have the setting of data being done in a componentDidUpdate
function on the menu. However, I've no luck in trying to get the component to detect the data changing for the key as an "update". Any suggestions on how to properly update the state of the menu each time this data is updated?
If your component lives anyway in the app shell (sounds like it for the menu) then instead of passing in a prop "couple" it to the global state container.
const Menu = () => {
const data = useGlobalState(m => m.data.foo);
return <b>Current value: {data && data.value}</b>;
};
Instead of the logic operation you could also use data?.value
if you already use the next gen / latest version of TypeScript.
Since this way you are "connected" to the global state container your component will re-render on change.
Hope that helps!
New Feature Proposal
For more information, see the
CONTRIBUTING
guide.Description
Currently we allow pilets to share data using the Piral API by calling
setData
andgetData
to store into some global key/value store. I'd like to see the ability to have some data set into this by the base piral instance on render.Background
The use case for this is for sharing user information to pilets. Currently, we expect the instance to render only to authenticated users. We pull some user information (such as email) from an API when rendering the Menu component in order to generate portions of the menu. When we do so, we'd like to have the ability to make the user information available to all the other pilets.
Discussion
Biggest pro here is being able to boootstrap data from the main piral instance and have that piral instance manage the data for the other pilets. This is useful for any sort of global data that isn't necessarily spawned/acquired by a specific pilet.