How to refresh code lenses in monaco editor - reactjs

I adding custom CodeLenseProvider to my React Monaco Editor, which works great in general:
{
provideCodeLenses(model: monaco.editor.ITextModel, token: monaco.CancellationToken): monaco.languages.ProviderResult<monaco.languages.CodeLensList> {
const lenses: monaco.languages.CodeLens[] = [];
lenses.push(...getLenses());
return {
lenses,
dispose: () => {},
};
},
}
The problem is that my method getLenses is time consuming and asynchronous. Ideally I'd like to e.g. fetch some data when provideCodeLenses is called, or even better, I'd like to be able to call provideCodeLenses manually refresh the lenses.
The steps would be:
I have onChange listetener on
<MonacoEditor onChange={onChange} .../>
this onChange is doing some async action
once the action is finished, I'd like to refresh code lenses
How can I achieve that? I know code lense provider has onDidChange property but it is not really clear to me how to use it or if it helps my case

Related

Is this correct way to render according to the state data using async await?

I got multiple buttons that render different modals.
The modals may render different results according to the data provided in the state.
I got 3 state brackets I need to consider
const [cartChangeStoreShow, setCartChangeStoreShow] = useState(false);
const [orderLineId, setOrderLineId] = useState('');
const [storeId, setStoreId] = useState('');
cartChangeStoreShow is for controlling the visible state of the modal
What I want to do is I wanna change OrderLineId and storeId before rendering the component.
The data will change according to the orderlineId and storeId.
The component is like this
<CartChangeStorePopUp
visibility={cartChangeStoreShow}
setCartChangeStoreShow={setCartChangeStoreShow}
orderLineId={orderLineId}
storeId={storeId}
/>
I am calling api inside CartChangeStorePopUp component according to prop data.
So I am handing the user press button like this.
<TouchableOpacity
onPress={() => renderCartChangeStore(cartItem)}>
<Text>
Change Store
</Text>
</TouchableOpacity>
const renderCartChangeStore = async cartItem => {
try {
await setOrderLineId(cartItem.orderLineId);
await setStoreId(cartItem.storeId);
} catch (err) {
console.log(err);
} finally {
setCartChangeStoreShow(true);
}
};
the code is working now but from what I read before
Async Await doesn't work properly with setState,So I wanna know if there is potential error with the code written here
To me, it does not make sense both the async/await presence and the try/catch/finally.
Async/await is useful when the function you're calling is dealing with something like I/O, time-consuming, where you cannot do anything than "wait" for the completion. Since "to wait" might be something not desirable in a UI context, the async/await pattern helps you to keep track to the "slow function", but even leave the CPU free to serve other useful tasks.
That being said, the "setXXX" functions of React.useState are not time-consuming: no I/O or similar task involves. Hence, the async/await is not applicable.
Going further, the "setXXX" functions of React.useState throw no error on setting. They're much like setting a variable like so:
var storeId = "";
function setStoreId(value) {
storeId = value;
}
That is, the try/catch/finally is quite useless.
If you want, you might optimize the code by grouping the three variables as a single immutable object. However, that's up to your real code.
const [storeState, setStoreState] = useState({
cartChangeStoreShow: false,
storeId: "",
orderLineId: ""
});
const renderCartChangeStore = cartItem => {
setStoreState({
cartChangeStoreShow: true,
storeId: cartItem.storeId,
orderLineId: cartItem.orderLineId,
});
};
Here is a more compact way to achieve the same behavior:
const renderCartChangeStore = cartItem => {
setStoreState({
cartChangeStoreShow: true,
...cartItem,
});
};
Bear in mind that is very important that you treat the storeState as immutable. That is, never ever change a field of the object, rather create a brand new object with the new field value.
At that point, the component should be called like so:
const handleCartChangeStoreShow = value => {
setStoreState({
...storeState,
cartChangeStoreShow: value,
});
}
<CartChangeStorePopUp
visibility={storeState.cartChangeStoreShow}
setCartChangeStoreShow={handleCartChangeStoreShow}
orderLineId={storeState.orderLineId}
storeId={storeState.storeId}
/>
Notice the handler to correctly alter the storeState object. Worthwhile mention how the new value is set. First, all the current storeState is copied to a fresh new object, then the new show value is also copied on the same object. However, since that happens after, it'll have an override-effect.

Not able to trigger function from event

I am not a skilled react programmer but still hope someone would care to explain what I am missing:
What I want
I would like to change accounts in Metamask, detect the "accountsChanged" event, and trigger the testFunction.
What works
I am able to trigger the testFunction by clicking the test function button.
I can detect account change (for some reason it is detected around 5 times every time I change).
What does not work
I am not able to trigger the testFunction upon account change and get the message TypeError: this.testFunction is not a function
Suspect there is something fundamental about react I am missing here...Thanks for all replies!
class App extends Component {
...
componentDidMount = async () => {
...
};
testFunction = async =>{
console.log("triggered the test function");
};
render() {
window.ethereum.on('accountsChanged', function (accounts) {
console.log("account change detected");
this.testFunction(); --> this is not working
});
return (
<div className="App">
<button type="button" onClick={this.testFunction}>test function</button>
</div>
);
}
}
You need to convert your normal function to arrow function. Because normal function derives this from the object which is calling it, but arrow function derives it's this from surrounding scope, hence in arrow function this will point to your class and will have access to the methods.
window.ethereum.on('accountsChanged', accounts => {
Also, you can continue using normal function, but in that case you can store the this in some other variable like that' or 'self and use it inside the normal function to call the methods of the class.
let that = this;
window.ethereum.on('accountsChanged', function(accounts){
that.testFunction() //this will work
I struggled to update the component of my app when an account was changed using MetaMask. What I did was what Vivek suggested: create a reference of this and then handle the callback. At the end my function using etherjs and the same event of metamask (ethereun.on('accountsChanged'..was this
const here = this
provider.provider.on('accountsChanged', function (accounts) {
console.log('Account changed!!')
here.currentAccount = accounts[0]
})
This code also work with Vue

How to detect if the React app is actually being viewed

So I have created an app that shows data in realtime obtaining it from devices.
However, I want to make my server not obtain data when nobody is viewing the app.
So essentially I need some way to determine whether the app is currently being viewed, regardless of if it's desktop or mobile, this includes tab is on focus where the app is opened and that is what the user is currently viewing, and there is nothing on top of the browser, so browser opened on the correct tab, but user has explorer on top of it doing something entirely different, this for my case should be false, and for mobile, the same thing including if device is locked (screen off).
The reason for trying to do that, is to reduce the load on the devices, so that data is being requested, only when there is someone to view it.
From what I have researched I found out about the focus and blur events, but I was unable to make it work, and I don't even know if that is the correct approach, but what I have tried is:
Adding event listeners to the window in the App component:
function App() {
useEffect(() => {
window.addEventListener("focus", () => { console.log("viewed")});
window.addEventListener("blur", () => { console.log("hidden")});
})
}
Adding them as props to the App component within index.js:
<App onFocus={() => {console.log("viewed")}} onBlur={() => {console.log("hidden")}}/>
Neither had any kind of effect, I didn't get either of the console outputs.
Is that even the correct approach?
I would add a socket connection to the app. Then the server would be able to know if there are at least X persons connected and act accordingly.
I would suggest you to try socket for this kind of connection tested, but since you also wanna reduce the load for the user, testing if the user is focused in the browser is the way to go.
To achieve it, I won't add this code inside the React component because of the nature of React as all of its components are rendered inside the <div id="root"></div>, other parts of the html page will still be unaffected by this mechanism. So what you probably want to do is to add the code in the index.html and use window.userFocused to pass the value into React from the index
Edit: added focus/blur script
<script>
window.addEventListener("focus", function(event)
{
console.log("window is focused");
window.userFocused = true;
}, false);
window.addEventListener("blur", function(event)
{
console.log("window is blurred");
window.userFocused = false;
}, false);
</script>
So I ended up solving it with pretty much the same code as initiallywith a few slight modifications, I still used an useEffect hook:
const onFocusFunction = () => {
// do whatever when focus is gained
};
const onBlurFunction = () => {
// do whatever when focus is lost
};
useEffect(() => {
onFocusFunction();
window.addEventListener("focus", onFocusFunction);
window.addEventListener("blur", onBlurFunction);
return () => {
onBlurFunction();
window.removeEventListener("focus", onFocusFunction);
window.removeEventListener("blur", onBlurFunction);
};
}, []);
The best way is to use document.
document.onvisibilitychange = () => {
console.log(document.hidden)
}

How does document.getSelection work under reactjs environment?

I was working on a electron-react project for epub file. Right now, I am planning to make the app capable of selecting text field and highlight it.
To achieve it, I was trying to use web's Window.getSelection api. However, there are some really weird things come up like in this.
In short, I am unable to capture the Selection object. It seems like even if I log the Selection object, this object could somehow jump to something else. Also, I cannot even serialize the Selection object with JSON.stringfy. This is super surprising, and this is my first time seeing something like this (I will get empty object to stringfy the Selection object).
So how to use Window.getSelection properly under react-electron environment? Or this api will not work well for text content which is generated by react's dangerouslySetInnerHTML?
Looks like the window.getSelection api needs to toString the selected object.
const getSelectionHandler = () => {
const selection = window.getSelection();
console.log("Got selection", selection.toString());
};
Super simple textarea and button to get selection react demo
this solution might help someone, catch last selection
const selection = useRef('');
const handleSelection = () => {
const text = document.getSelection().toString();
if (text) {
selection.current = text;
}
}
useEffect(() => {
document.addEventListener('selectionchange', handleSelection);
return () => {
document.removeEventListener('selectionchange', handleSelection);
};
});

How to delay redux state changes to allow for a side effect in a react component

Sorry for the kind of vague title. The best way to explain my question might be an example.
I have a of items in redux, and the list is displayed in a react component using standard react-redux connected components. Each individual item has a button, which when clicked, does some asynchronous work, and then removes the item from the list and puts it in another list displayed somewhere else. It's important that the logic for starting the asynchronous work be handled in redux because it's important to the state of my application.
That basic functionality works, but now I want to add feedback to the button so that when the side effect succeeds, it changes the label to a Checkmark (for simplicity, i'll do nothing and leave the list unchanged if the request fails in this example). The item will stick around for an extra second with the checkmark before being removed from the list.
The problem is that if i remove the item from the list as soon as the async work is done, it is immediately unmounted, so I need to delay that. I've been trying to come up with a way to implement this logic that is reusable across my app, as I'll want the checkmark feedback in other unrelated parts of the app.
The simple solution is to dispatch an action on success that just changes the state to indicate that the item's request succeeded, and then do a setTimeout to dispatch another action 1 second later to actually remove the item from the list.
I feel like doing that logic will become very repetitive if i do it in different places across my app where I have a button. I'd like to be able to not have to repeat the timeout logic for every new button that needs this. But I want what my app displays to represent the current state of my app.
Has anyone dealt with an issue like this before?
Thanks
Edit: I don't think it should really change the general solution, but I'm using redux-loop to handle side effects. I feel like a generic solution will work fine with thunk or saga or whatever else though.
You mentioned that you are using redux-loop to handle your async stuff. I'm more familiar with redux-thunk, so if it's ok with you, I'll give you an answer that uses a thunk.
You can keep your code DRY if you put the timeout in your action creator, and then call that action creator from multiple buttons:
// actionCreators.js
const fetchTheThings = (url, successAction, failureAction, followUpAction) => (dispatch) => {
//if you put the app in an intermediate state
//while you wait for async, then do that here
dispatch({ type: 'GETTING_THINGS' });
//go do the async thing
fetch(url)
.then(res => {
if (res.status === 200) return res.json();
return Promise.reject(res);
})
.then(data => {
//on success, dispatch an action, the type of which
//you passed in as an argument:
dispatch({ type: successAction, data });
//then set your timeout and dispatch your follow-up action 1s later
setTimeout(() => {
dispatch({ type: followUpAction });
}, 1000);
})
.catch(err => {
//...and handle error cases
dispatch({ type: failureAction, data: err });
});
};
//then you can have exported action creators that your various buttons call
//which specify the action types that get passed into fetchTheThings()
export const actionFiredWhenButtonOneIsPressed = () => {
return fetchTheThings('<some url>', '<success action>', '<failure action>', '<follow-up action>');
};
export const actionFiredWhenButtonTwoIsPressed = () => {
return fetchTheThings('<other url>', '<other success action>', '<other failure action>', '<other follow-up action>');
};
Hopefully that at least gives you some ideas. Good luck!
Ian's solution should be generalizable pretty well, but maybe, if you can live with a success confirmation that doesn't require DOM activity:
A simple unmount style that turns the element green and then fades it out, would be sufficient for a satisfying user feedback to tell that stuff has worked out.

Resources