I'm encountering some weird behavior in a custom React useMemo hook. I'm trying to confirm that the correct values are being returned (as there's some evidence elsewhere that I'm getting the wrong value back), but I can't inspect using a developer console because the code is getting injected in a way that I can't attach to. (Oh well.)
I have a custom hook defined as follows (with all the relevant console.log statements in place for debugging):
const defaultMargins = {...}
export const usePanelDimensions = (width: number, height: number, panelCount: number, panelSpacing: number, margins?: Margins) => {
return useMemo(() => {
console.log(`Default margins are ${JSON.stringify(defaultMargins)}`)
console.log(`Received margins are ${JSON.stringify(margins)}`)
console.log(`Received width ${width} and height ${height}`)
const netHorizontalMargin = (margins?.left ?? defaultMargins.left) + (margins?.right ?? defaultMargins.right)
const netVerticalMargin = (margins?.top ?? defaultMargins.top) + (margins?.bottom ?? defaultMargins.bottom)
const netPanelSpacing = (panelSpacing * (panelCount - 1))
console.log(`Net margins: ${netHorizontalMargin} ${netVerticalMargin} ${netPanelSpacing}`)
const panelWidth = width - netHorizontalMargin
const panelHeight = (height - netVerticalMargin - netPanelSpacing) / panelCount
console.log(`-------`)
console.log(`** panel width ${panelWidth}`)
console.log(`** should report panel width as ${width - netHorizontalMargin}`)
console.log(`** Returning panel width ${panelWidth} and panel height ${panelHeight}`)
console.log(`-----`)
return {panelWidth, panelHeight}
}, [width, height, panelCount, panelSpacing, margins])
}
So here we are trying to divide a drawing space into one or more equal-sized bands, offset by some vertical distance.
We receive the overall canvas dimension (read from the rendered component), as well as a number of panels, the space between the panels, and some optional margins. If the margins aren't set, we use some defaults.
We compute the total width of one panel as the input width, less the left and right margins. The height of each panel is the height of the canvas, less the top and bottom margins, then divided into panelCount equal-sized units with panelSpacing pixels between them.
But when I run this, I get output for all the console.log statements except the ones between the dashed lines. Those statements are skipped entirely. Not "the variable interpolation leaves the values out," just the console log doesn't happen at all.
I understand that React sometimes suppresses console logging on second render when StrictMode is on (see https://camunda.com/blog/2021/02/be-careful-with-console-log-when-using-react-strictmode/) but I don't think strict mode is active here.
I considered that the problem might have something to do with some of the values not being set on first render. However, the line ** should report panel width ... is printing a value (width - netHorizontalMargin) which is a direct computation on two values that both get printed successfully a few lines up.
I'm kind of at a loss as to a) why these output statements are getting ignored, and b) how to confirm the value that's actually getting returned, as the hook's consumer also can't display information about these computed values.
Edit
CodeSandbox link: https://codesandbox.io/s/hidden-cdn-0k7chz?file=/src/App.js
Not quite a MWE because obviously there's more code here than there needs to be, but this will show the behavior in question. The code at the CodeSandbox link runs without a problem (printing out the computed values between the --- sections) on Firefox 104.0.1 (Ubuntu snap), where I get output:
Default margins are {"left":30,"right":15,"top":20,"bottom":50}
Received margins are {"left":50,"right":25,"top":30,"bottom":50}
Received width 2700 and height 475
Net margins: 75 80 0
-------
** panel width 2625
** should report panel width as 2625
** Returning panel width 2625 and panel height 395
-----
The issue does reproduce on Chrome 105.0.5195.102 (official Ubuntu 64-bit build), where I see output:
Default margins are {"left":30,"right":15,"top":20,"bottom":50}
Received margins are {"left":50,"right":25,"top":30,"bottom":50}
Received width 2700 and height 475
Net margins: 75 80 0
-------
-----
Before I go filing a bug report against React and/or Chrome, though, has anybody else encountered this behavior or have an explanation for why it might be expected/intended?
Looks like this was down to an old filtering statement in the console. The keywords in question probably shouldn't have impacted these variables, but removing it clears up the problem, so there you are.
Related
I am aiming to make messages in a Microsoft Bot Framework Chatbot appear more like 'speech bubbles' and have a triangular 'nub' display to the side of each, eg
The React Webchat client provided as part of the Bot Framework has optional properties to control the size and position of nubs which are
bubbleNubOffset?: number | 'bottom' | 'top';
bubbleNubSize?: number;
My first question is: Regardless of what (positive) bubbleNubOffset value is used, the nub always appears at the top of the message bubble rather than being offset eg
How can I move the nub down to appear more like the first image? Setting bubbleNubOffset to any negative number moves the nub to the bottom of the message bubble.
The source in GitHub for them has a comment
/**
* Nub offset ''bottom' will render nub at the bottom
* A positive or negative number will shift nub offset up/down
* "top" is equivalent to positive zero.
* "bottom" is equivalent to negative zero.
*/
but it doesn't appear to actually work like that.
My second question is: Without adding CSS to set the z-index of the message element, setting the bubbleNubSize causes the nub to be displayed in front of the message eg:
Is there a way to avoid this via configuration or is custom CSS the only way to do it?
My guess is you are supplying too large of a value to the bubbleNubSize property. For instance, here are the values that I use in my test Web Chat client.
bubbleBackground: 'black',
bubbleBorderColor: 'red',
bubbleBorderRadius: 9,
bubbleBorderWidth: 2,
bubbleTextColor: 'white',
bubbleNubSize: 5,
bubbleNubOffset: 'bottom'
bubbleFromUserBackground: 'black',
bubbleFromUserBorderColor: 'green',
bubbleFromUserBorderRadius: 9,
bubbleFromUserBorderWidth: 2,
bubbleFromUserTextColor: 'white',
bubbleFromUserNubSize: 5,
bubbleFromUserNubOffset: 'top'
Thankfully, Web Chat has built-in logic that helps interpret how the bubble and nub displays based on the values you provide. For instance, the nub mirrors vertically depending on the offset, and the border radius of the nub corner to be used is set to 0 to create a visually seamless flow.
I'm using recharts library to create a chart. The x axis of the chart is the timeline axis and I use timestamps as values for it. What I'm trying to do is to display only 5 ticks(for example) spaced equally. I found a lot of people having similar problems on the library git repository. One fixes was to use scale="time" and setting a minimum tick gap for x axis:
<XAxis dataKey='timestamp'
domain={['dataMin', 'dataMax']}
type = 'number'
scale="time"
minTickGap={120}
tickCount={7} */
tickFormatter = {(unixTime) => moment(unixTime).format('DD MMM')}
/>
I'm using the brush component of the library to zoom in on the chart and the problem with this implementation is that when I zoom in sometimes it displays all the ticks.
Ex. without zoom in:
Ex zooming in:
I found that if I remove the scale="time" parameter and set a specific tickCount and set interval to 0 the problem that occurs when I zoom in disappears but 1 tick is missing or it appears only when I zoom in.
<XAxis dataKey='timestamp'
domain={['dataMin', 'dataMax']}
type = 'number'
interval={0}
tickCount={7}
tickFormatter = {(unixTime) => moment(unixTime).format('DD MMM')}
/>
Does anyone now how can I fix the issues mentioned above? I don't think I can use predefined ticks based on maximum and minimum values of the axis because those values becomes irrelevant when I zoom in.
There is no cure for it as far as I know. You could mention all your tick explicitly in ticks param like ticks={[10,20,50,200,500,1000]} and they will do just fine
Another workaround for me is to set domain as
domain= {['auto','auto']}
and it shows all the ticks correctly.
I'm working on a somewhat long-in-the-tooth AngularJS 1.2 application that uses angular-google-charts as a wrapper. I've tried and succeeded at getting the chart to display the way I want it to when I emulate the print media in Chrome Inspector. However, when I actually go to print, the chart is reduced to a 400 x 200 w/h ratio, and the labels start to overlap.
I've used the chartArea options in the chart object to fix the print version of the chart to 400 x 600, with a small offset for the vAxis, and, again, it works when I am developing from the emulated print view.
I've also attempted to use the PNG-generating getImageURI() method, which I can resize, but the resolution stays the same - so I get a grainy PNG with labels overlapping.
I've also attempted a callback method to re-set the chartOptions on load, and I'm able to reset the overall size, but I don't see how to do the chartArea with a callback.
let resizeChart = {'height': 456, 'width': 678};
$scope.resizeChartPrintOptions = function (chartWrapper) {
chartWrapper.setOption("height", resizeChart['height']);
chartWrapper.setOption("width", resizeChart['width']);
console.log('chartWrapper: ', chartWrapper); // this shows chart size but not chart area
}
I'm at a loss for how to debug the print preview, other than to use the Chrome Inspector render emulator, which isn't serving me the way I expected.
How can I print a Google Chart to the desired size?
Im using leaflet to create a photo map, with my own tiles, which works as expected.
Im trying to work out how I can prevent the zoom from following this Quadtree type pattern:
Zoom Level 0 - Entire map width = 256px;
Zoom Level 1 - Entire map width = 512px;
Zoom Level 2 - Entire map width = 1024px;
And so on...
I would like to be able to zoom in say increments of 25% or 100px.
An example of 100px increments:
Zoom Level 0 - Entire map width = 200px;
Zoom Level 1 - Entire map width = 300px;
Zoom Level 2 - Entire map width = 400px;
And so on...
Question:
What is the logic for doing this? If it is at all possible?
My reason for wanting to do this is so that my photo map (which doesnt wrap like a normal map) can be more responsive and fit the users screen size nicely.
I made a demonstration of my issue which can be seen here
The short answer is that you can only show zoom levels for which you have pre-rendered tiles. Leaflet won't create intermediary zoom levels for you.
The long answer is that in order to use do this, you need to define your own CRS scale method and pass it to your map, for example:
L.CRS.CustomZoom = L.extend({}, L.CRS.Simple, {
scale: function (zoom) {
// This method should return the tile grid size
// (which is always square) for a specific zoom
// We want 0 = 200px = 2 tiles # 100x100px,
// 1 = 300px = 3 tiles # 100x100px, etc.
// Ie.: (200 + zoom*100)/100 => 2 + zoom
return 2 + zoom;
}
});
var map = L.map('map', { crs: L.CRS.CustomZoom }).setView([0, 0], 0);
In this example, I've extended L.CRS.Simple, but you can of course extend any CRS from the API you'd like, or even create your own from scratch.
Using a zoom factor which results in a map pixel size that is not a multiple of your tilesize, means your right/bottom edge tiles will only be partially filled with map data. This can be fixed by making the non-map part of such tiles 100% transparent (or same the colour as your background).
However, it is, in my opinion, a much better idea to set the tilesize to match the lowest common denominator, in this case 100px. Remember to reflect this by using the tileSize option in your tile layer. And, of course, you will need to re-render your image into 100x100 pixels tiles instead of the 256x256 tiles you are using currently.
One caveat, the current version of LeafletJS (0.5) has a bug that prevents a custom scale() method from working, due to the TileLayer class being hardcoded to use power-of-2 zoom scaling. However, the change you need to do is minor and hopefully this will be addressed in a future release of Leaflet. Simply change TileLayer._getWrapTileNum() from:
_getWrapTileNum: function () {
// TODO refactor, limit is not valid for non-standard projections
return Math.pow(2, this._getZoomForUrl());
},
To:
_getWrapTileNum: function () {
return this._map.options.crs.scale(this._getZoomForUrl());
},
I am trying to use the TransformToVisual method to get an Image element's absolute coordinates like this:
var transform = img.TransformToVisual( this ); // this is the current page
Point absolutePosition = transform.Transform( new Point(0, 0) );
For the first time I use it, this code returns an incorrect value (it is always 155,255). However, it always returns the correct coordinates afterwards.
This code runs in a Tap event handler, so the UI is already completely rendered when it is being executed. I tried calling InvalidateMeasure and InvalidateArrange (both on the Image element and on the page), but it did not help (I don't know if those calls have any connection whatsoever with TransformToVisual).
What could be the problem?
UPDATE: If I pin lock my phone and then unlock it, then the above issue does not happen.
UPDATE2: The position seems to be where the upper left hand corner of the image would be if it was at the center of the page. (The image's size is fixed 170x260. I get the coordinates 155,255. 2*155 + 170 = 480, 2*255 + 260 = 770 (okay the second one is not exactly 800, but it is suspicious anyway. The Application bar and the System tray are both hidden.))
UPDATE3: The problem occurs on the main page of my app. However, if I navigate to another page, then come back to the main page, it also works correctly.