Is it possible to replace the currently initialized state machine in React?
For example, I initialize a state machine via Provider with a dummy configuration. Then, upon entering a specific page in the app, I update it via context.
Background of what I want to achieve: Consumer can either load the configuration globally or page-specific. Let's say for example that a required data for your config is unknown until you reach a specific portion of your page. So, only then you can properly create a state machine. Or, if consumer wants to keep the configuration per module/page. Bec doing this at the top level would mean, you have to collate all configurations which can get lengthy and hard to debug assuming you know the configuration in advance.
In provider
const dummyConfig = {
id: 'init',
initial: 'init_state',
states: {
init_state: {
on: {},
},
},
};
const initMachine = createMachine(dummyConfig);
const [stateMachine, setStateMachine] = useState(initMachine);
// stateMachine does not get updated with the new machine from the child component below
// passed via setMachine callback in the context
const [current, send, service] = useMachine(stateMachine);
const updateMachine = useCallback((machine: StateMachine) => {
setStateMachine(machine);
}, []);
return (
<MachineContext.Provider value={[current, send, service, updateMachine]}>
{children}
</StateMachineContext.Provider>
);
In child component somewhere in the app
const machine = createMachine(newConfig);
// Context hook
const { current, send, servicee, updateMachine } = useStateMachine();
// I can receive the new machine up in the provider (checked via console log),
// but it does not update the machine
// The value of current remains that from the first created machine
updateMachine(machine);
Related
I am developing an app in ReactNative offline.
One of the functionalities is to use bluetooth to synchronize the data (that the app was collecting) with other devices that use the same app.
I started to develop this task with the react-native-ble-manager library, i can connect from device A to device B, but I don't understand how to listen to the incoming connection in device B. I need to know this to show a certain view.
can anybody help me?
I am using the correct library?
Thanks!
You can't use only the react-native-ble-manager for this project. The library states in its readme that it is based on cordova-plugin-ble-central, which can only act as a central. For a BLE connection you need a central and a peripheral.
Take a look at react-native-peripheral. It allows you to act as a perihperal, create a characteristic with some data, add it to a service and register it so other devices can find it. This is their usage example:
import Peripheral, { Service, Characteristic } from 'react-native-peripheral'
Peripheral.onStateChanged(state => {
// wait until Bluetooth is ready
if (state === 'poweredOn') {
// first, define a characteristic with a value
const ch = new Characteristic({
uuid: '...',
value: '...', // Base64-encoded string
properties: ['read', 'write'],
permissions: ['readable', 'writeable'],
})
// add the characteristic to a service
const service = new Service({
uuid: '...',
characteristics: [ch],
})
// register GATT services that your device provides
Peripheral.addService(service).then(() => {
// start advertising to make your device discoverable
Peripheral.startAdvertising({
name: 'My BLE device',
serviceUuids: ['...'],
})
})
}
})
There is a section about dynamic values where they explain how you can use the onReadRequest and onWriteRequest callbacks to listen to read and write operations on the peripheral and even return dynamic values on each read request:
new Characteristic({
uuid: '...',
properties: ['read', 'write'],
permissions: ['readable', 'writeable'],
onReadRequest: async (offset?: number) => {
const value = '...' // calculate the value
return value // you can also return a promise
},
onWriteRequest: async (value: string, offset?: number) => {
// store or do something with the value
this.value = value
},
})
I'm using Truffle to develop DAPP. I would like to ask if it's possible to dynamically get the network name during deployment process with dashboard as a spcified network. What I mean by that is I have a deploy-config.js file which holds different configurations for different networks. I also have a 2_deploy_MyContract.js migration file. MyContract expects a struct in the constructor function as a parameter.
const MyContract = artifacts.require('MyContract');
const getConfig = require('../deploy-config');
module.exports = async function (deployer) {
const config = getConfig(currently_selected_network); <-- The Problem
await deployer.deploy(
MyContract,
{
...config.data
}
);
};
When I run truffle migrate --reset --network dashboard I can change the selected network using metamask any time. I would like to somehow fetch the network name it deploys to and pass it as currently_selected_network so my js function can provide proper config values. I think I can try specifying the network name by updating the truffle-config.js file and then only deploy to those predefined networks but using dashboard allows me to not keep the mnemonic inside the repo and sign every transaction by Metamask extension.
If you have any other ideas how to achive this goal I will be more than happy to hear it out!
This is how deploy-config.js looks like
const config = {
network1: {
paramA: "A"
paramB: "B"
},
network2: {
paramA: "C"
paramB: "D"
}
}
function getConfig(networkName) {
switch(networkName) {
case "network1":
return config.network1;
case "network2":
return config.network2;
default:
return null;
}
module.exports = getConfig
On your migration scripts add, network to your anonymous function
I believe order and arg position matter.
e.g.
module.exports = async (deployer, network, accounts) => {
console.log(network);
console.log(accounts);// also useful to have this at hand
}
I’m using react-native-offline library in my app for offline queueing of requests to server and the action changeQueueSemaphore had caused me few difficulties.
I’ll try explaining by an example.
Let’s say my app posts offline issues in Stack Overflow by three basic actions:
const createIssue = (issue) => action("CREATE_ISSUE",{issue},{retry: true});
const addComment = (comment) => action("ADD_COMMENT",{comment},{retry:true});
const closeIssue = (timeOfClosing) => action("CLOSE_ISSUE", {timeOfCLosing}, {retry: true});
When my app works in offline mode the actions are entered by order to the queue and then being dispatched one after another when network connection is regained.
In order to dispatch the last two actions I first need to dispatch the create issue action and get a generated id from my server. Thus, when listening to createIssue action using redux saga,
in the handler I'm dispatching changeQueueSemaphore('RED') -> sends post request of creating an issue -> retrieving the id from the response and saving it in state, then releases the queue again:
function* createIssueHandler(action: ActionType<typeof createIssue>) {
const {issue} = action.payload;
const {changeQueueSemaphore} = offlineActionCreators;
// stops the queue before generating id
yield put(changeQueueSemaphore('RED');
try {
const id = // Api Call to create Issue
// saves the id in state
yield put(createIssueSuccess(id));
// releases queue again
yield put(changeQueueSemaphore('GREEN');
} catch(err) {
// error handeling
}
function* addCommentHandler(action: ActionType<typeof addComment>) {
const issueId = yield select(getIssueIdFromState);
enter code here
// api call to `SERVER_URL/addComment/${issueId}`;
}
function* closeIssue(action: ActionType<typeof closeIssue>) {
// same as addCommentHandler
}
After looking at the action logs I saw that addComment and closeIssue actions are being dispatched before changeQueueSemaphore causes the queue to stop.
Any help will be pleased <3.
I am having trouble getting dependency injection working for my AuthorizerService. Obviously, dep-inj is not ready until after Aurelia "starts", but I wasn't sure how to access it.
main.js:
aurelia.container.registerInstance(HttpClient, http.c());
// set your interceptors to take cookie data and put into header
return aurelia.start().then(() => {
let Authorizer = new AuthorizerService();
aurelia.container.registerInstance(AuthorizerService, Authorization);
console.log('Current State: %o', Authorizer.auth);
Authorizer.checkCookieAndPingServer().then(() => { console.log('Current State: %o', Authorizer.auth); aurelia.setRoot(PLATFORM.moduleName('app')); }, () => { aurelia.setRoot(PLATFORM.moduleName('login-redirect')); });
});
Now the problem is that if I do "new AuthorizerService()" then "this.http.fetch()" is not available in AuthorizerService.js.
Am I meant to pass "http.c()" (which delivers the HttpClient instance) as a parameter inside:
checkCookieAndPingServer(http.c())
or is there another way?
Can I delete "new AuthorizerService()" and just do (I made this up):
aurelia.container.getInstance(AuthorizerService);
Somehow FORCE it to do dependency-injection and retrieve the "registered Instance" of "http.c()"?
I can't just check cookie. I have to ping server for security and the server will set the cookie.
I think this is all sorts of wrong, because I need a global parameter that just is false by default, then it does the query to backend server and setsRoot accordingly. Perhaps only query backend in the "login page"? Okay but then I would need to do "setRoot(backtoApp); aurelia.AlsoSetLoggedIn(true);" inside login module. But when I setRoot(backtoApp) then it just starts all over again.
In other words, when setRoot(login); then setRoot(backToApp); <-- then AuthorizerService instance doesn't have its proper data set (such as loggedIn=true).
EDIT: Better Solution maybe:
main.js:
return aurelia.start().then(() => {
let Authorizer = aurelia.container.get(AuthorizerService);
let root = Authorizer.isAuthenticated() ? PLATFORM.moduleName('app') : PLATFORM.moduleName('login');
console.log('Current State: %o', Authorizer.auth);
aurelia.setRoot(root);
});
Authorizer.js
constructor(http) {
this.http = http;
this.auth = {
isAuthenticated: false,
user: {}
}
}
"this.auth" is no longer static. No longer "static auth = { isAuthenticated: false }" which was some example code I had found.
So now "auth" gets set inside "login" module. But this means the "login" module is displayed every single time the app loads briefly, before being redirected back to "setRoot(backToApp)"
If the class you want to get the instance is purely based on service classes and has no dependencies on some Aurelia plugins, it doesn't need to wait until Aurelia has started to safely invoke the container.
For your example:
aurelia.container.getInstance(AuthorizerService);
It can be
aurelia.container.get(AuthorizerService);
And you should not use new AuthorizerService(), as you have noticed in your question.
I am having an issue dealing with multiple realms in React Native. I'm working on an app that allows users to use the app without having a subscription (using a local realm) and then at any point in their app journey they have the option of upgrading to a subscription with syncing (which uses sync to a realm object server).
When I start the app I check to see if they are using sync and if so I initialize a synced realm with their user and everything works great. I get all the data I expect.
However, when the app starts on first launch after install (that part about first launch after install is crucial) and I see that they don't use sync I initialize a local realm which I save data to until they decide to log in to their sync account (if they have one). At this point I attempt to pull information from the synced realm but it does not have the information that I saw when I only initialized the synced realm (in the case that on app startup I detect they use sync).
I am able to log in as the sync user but the data isn't there if I've previously initialized a local realm AND this logic gets run on the first launch of the app after install. The data only shows up from the realm object server when I initialize a local and synced realm on a secondary launch of the app (no reinstall before launching).
Here's a simple test script with dummy data in it with which I've been able to replicate the observed behavior:
const username = 'testuser2';
const password = 'supersecret';
const tld = 'REALM_OBJECT_SERVER_TLD';
class Test extends Realm.Object {}
Test.schema = {
name: 'Test',
properties: {
id: {
type: 'string',
},
}
};
function initLocalRealm() {
return new Realm({
path: 'local.realm',
schema: [Test],
});
}
function initSyncedRealmWithUser(user) {
return new Realm({
path: 'synced.realm',
sync: {
user,
url: `realm://${tld}:9080/~/data`,
},
schema: [Test],
});
}
function writeTestObjectWithId(realm, id) {
realm.write(() => {
realm.create('Test', {
id,
});
alert(`Test object with id: ${id}`);
});
}
initLocalRealm();
// setup
// uncomment this and comment out the login section to setup user on first run
// Realm.Sync.User.register(`http://${tld}:9080`, username, password, (error, user) => {
// if (error) {
// return;
// }
// const syncedRealm = initSyncedRealmWithUser(user);
// writeTestObjectWithId(syncedRealm, '1');
// });
// login
Realm.Sync.User.login(`http://${tld}:9080`, username, password, (error, user) => {
if (error) {
return;
}
const syncedRealm = initSyncedRealmWithUser(user);
alert(`Synced realm test objects: ${syncedRealm.objects('Test').length}`);
});
If you create a react native app and then add this code to the main components componentDidMount function you should see that on the first run of the app (after you've uncommented the register code once) you will see the Test collection length at 0, but then when you refresh you will see the Test collection length at 1.
Any help on this would be awesome.
Thanks!
running your code snippet, I get a length of 1 immediately as soon as I uncomment the login section. Could you try observing your synchronized realm with the Realm Browser and see if it seems to have the data you are expecting after registering the user?