How does React autogenerate keys in nested lists? - reactjs

I have a case of highly nested keys within a React component.
import * as React from "react";
import { Lg } from "./lg.jsx";
import { L } from "./l.jsx";
import { Caesura } from "./caesura.jsx";
export const Poem = ({ poem }) => {
return poem.map(stanza => <Lg>{
stanza.map(line => <L>{
line.map((segment, index) =>
<React.Fragment key={segment}>{
index === 0 ?
segment :
<><Caesura />{segment}</>
}</React.Fragment>)
}</L>)
}</Lg>);
};
export default Poem;
I am not at all sure how React will handle keys in this case. I might not be able to come up with a better solution.
I am rendering poetry in a highly regular format (for other cases I used custom mdx.)
And did those feet in ancient time
Walk upon Englands mountains green:
And was the holy Lamb of God,
On Englands pleasant pastures seen!
And did the Countenance Divine,
Shine forth upon our clouded hills?
And was Jerusalem builded here,
Among these dark Satanic Mills?
Ought to be result in a tree of components like
<Lg>
<L>And did those feet in ancient time</L>
<L>Walk upon Englands mountains green:</L>
<L>And was the holy Lamb of God,</L>
<L>On Englands pleasant pastures seen!</L>
</Lg>
<Lg>
<L>And did the Countenance Divine,</L>
<L>Shine forth upon our clouded hills?</L>
<L>And was Jerusalem builded here,</L>
<L>Among these dark Satanic Mills?</L>
</Lg>

Related

React Tabulator - How to display table horizontally

I'm using react-tabulator for a component: http://tabulator.info/docs/4.0/frameworks
I have put the component on the page in my app but am struggling to do anything with the styling. Right now, the component just displays everything vertically and looks really bad:
I want to display this horizontally in something that looks like a normal tabular format. I would also like to change column width. I've found limited documentation examples. Someone did ask a similar question and in this StackOverflow thread: How to style react-tabulator table? but I've not been able to edit the styles.css stylesheet to do anything useful.
Here is my component code:
import React from 'react'
import { ReactTabulator } from 'react-tabulator'
import 'react-tabulator/lib/styles.css';
const TabularData = (props) => {
const dataArray = []
//gets just first streelights record
for (const [i, v] of props.streetlights.features.entries()) {
if (i < 1) {
dataArray.push(v.properties); // properties of each item is what contains the info about each streetlight
}
}
let columns = [
{title:"WORKLOCATI", field:"WORKLOCATI"},
{title:"WORKREQUES", field:"WORKREQUES"},
{title:"WORK_EFFEC", field:"WORK_EFFEC"},
{title:"WORK_REQUE", field:"WORK_REQUE"},
]
return (
<ReactTabulator
columns={columns}
layout={"fitData"}
data={dataArray}
/>
)
}
export default TabularData
The css in react-tabulator/lib/styles.css is just the most base-level css.
Try importing one of the pre-built themes:
import "react-tabulator/css/bootstrap/tabulator_bootstrap.min.css";
There are a whole bunch of them in the css folder, and you can use them as a basis for creating your own.
Minimum working example here.
To get the right styling you will also have to import tabulator.min.css in your module, which is the theme, according to here.
Your imports should look like this:
import { ReactTabulator } from 'react-tabulator'
import 'react-tabulator/lib/styles.css';
import 'react-tabulator/lib/css/tabulator.min.css'; // theme
Without it, it looks like the image you posted:
With it, it looks like this:
In the folder node_modules/react-tabulator/css you can find more themes.

Typescript useState React Hook Destructuring Error (Not Returning Array?)

I'm futzing about with a simple React app for the first time, and looking to set up a simple MVC to connect with an ASP.NET backend, all being done in Visual Studio 2019 (which has caused some headaches with React). However, now that I'm trying to implement models, I'm finding that all of the suggestions for strongly typing the useState hook aren't working for me.
According to a few tutorials like this one or this one, it should be as simple as adding a type in brackets, like const [state, setState] = useState<Type>({}), since it has a generic implementation. Unfortunately, for me, this throws the error "Uncaught TypeError: Invalid attempt to destructure non-iterable instance."
This thread here suggested that switching from an array to an object by changing the [] to {}, however that simply made the two parameters I passed be undefined, so I had no state or way to update said state.
I went down the path of reading until I had a brief understanding of array destructuring, so I understand that the idea is to pass an array with two constants that will be assigned in-order the elements of the array returned by the useState hook. So, I tried manually destructuring the array, setting a constant useState[0] and useState[1] separately. When I tried this for an untyped hook, it worked as expected. When I tried this for the typed hook, I got some errors about there not being elements, and upon printing out the object, found not an array like the untyped hook, but a boolean. It seems that a typed useState is returning the value "false" instead of an array.
At this point, my best guess is that I have some dependencies that don't implement the typed useState, but I'm really hitting a stone wall on troubleshooting. Anyone have some idea on what I'm doing wrong?
Edit: The testing file I have set-up -
import React, { useState } from 'react'
import 'bootstrap/dist/css/bootstrap.min.css';
import { Product } from '../Models/Product';
const Account = () => {
//Works as-intended
const test = useState(5);
//Returns false when logged
const test2 = useState<Product>({
"ProductID": "p#corn", "Description": "Delicious corn", "ImageLink": "https://testlink.com", "Name": "Corn", "Price": "2.99", "Category": "Food"
});
//What should work, to my understanding, however this makes the route crash when it's navigated to because of the "Inavlid attempt to destructure non-iterable instance
const [test3, setTest] = useState<Product>({});
function clickHandler() {
console.log(test)
}
function clickHandler2() {
console.log(test2)
}
return (
<div className='wrapper'>
<button onClick={clickHandler}>Test</button>
<button onClick={clickHandler2}>Test2</button>
</div>
)
}
export default Account;
The Product model -
export interface Product {
ProductID: string;
Description: string;
ImageLink: string;
Name: string;
Price: string;
Category: string;
}
Here's a CodeSandbox with an example somewhat related to your case, which demonstrates how useState is meant to work on everyone else's machine.
As you will see from testing it and messing with the example I shared, useState with a generic type specified should be fine and dandy and behave just as you expect.
That underlines that there's something about your local machine or project environment which is special to you.
import React, { useCallback, useState } from "react";
import "./styles.css";
interface Product {
Name: string;
Price: number;
}
const productA = {
Name: "Corn",
Price: 2.99
};
const productB = {
Name: "Frozen Orange Juice",
Price: 10_000
};
export default function Show() {
const [product, setProduct] = useState<Product>(productA);
const toggleProduct = () =>
setProduct(product === productA ? productB : productA);
return (
<div className="App" onClick={toggleProduct}>
<h1>{product.Name}</h1>
<h2>{product.Price}</h2>
</div>
);
}
Since you asked about how to get the best responses...
Ideally when posting try to create a problem case which other people can see failing. If you attempt to do this (e.g. using a CodeSandbox) but it works fine there, then it's a matter of working on cleaning up your own local environment until you figure out what is unique about your setup which makes code break for you but no-one else.
Ideally if you can't create it within a single page repro (sandbox), because it's something to do with your project structure, create a runnable reproduction by sharing the actual project structure and its code and configuration publically on github so people can recreate your problem by checking it out and building/running it. That way they will soon be able to track down what's distinctive about your project.
Often this process of creating a repro will help you figure out the problem anyway - e.g. you start with a vanilla create-react-app typescript skeleton from https://create-react-app.dev/docs/adding-typescript/ to populate a blank github repo and your problem case suddenly starts working there - now you have a basis for bisecting your problem by adding your project's special features to the simple repro until it breaks.

How to render components dynamically in Gatsby.js and use code-splitting?

I am building websites with Gatsby and Contentful and it has been great so far. My problem is, that I don't know how to dynamically render components based on the data in Contentful.
Let's say there is a page content type which as a title, url and field for components. Those components could be a YouTube player, or markdown text or photos. Currently I'm using a custom component that imports all available components and then renders the components of the page using switch.
switch (contentType) {
case 'billboard':
return <Billboard key={key} id={key} {...section.fields}/>;
case 'photos':
return <Photos key={key} id={key} {...section.fields}/>;
case 'postGrid':
return <PostGrid key={key} id={key} {...section.fields}/>;
case 'splitView':
return <SplitView key={key} id={key} {...section.fields}/>;
case 'text':
return <Text key={key} id={key} {...section.fields}/>;
case 'tile':
return <Tile key={key} id={key} {...section.fields}/>;
default:
return null;
}
The problem with this is that Gatsby will include all available components in the webpack chunk which leads to a blown up site if there are many of them. Let's say there is a page with text only (e.g. imprint) the YouTube player and photos component would be loaded too - just not used.
So... is there a way to render components based on data which then results in proper code-splitting?
Thank you so much!
I'm thinking of another approach; in a mapping component that renders your type of component based on contentType, much more cleaner and especially, a huge performance improvement (no need to force the code to check for the switch statement each time).
Without seeing the rest of the code it's difficult to guess how you are printing that switch. However, let's say you have all your data inside data object, then:
import SwitchComponents from '../../SwitchComponents/SwitchComponents'
// other code
return data.map(item => {
const Switcher = SwitchComponents[item.contentType];
return (
<Switcher
key={item.key} // or something unique
{...section.fields}
/>
);
})
This component should have a similar structure, such as:
import Billboard from './Billboard/Billboard';
import Photos from './Photos/Photos';
import PostGrid from './PostGrid/PostGrid';
import SplitView from './SplitView /SplitView';
import Text from './Text/Text';
import Tile from './Tile/Tile';
const SwitchComponents= {
billboard : Billboard,
photos : Photos,
postGrid : PostGrid,
splitView : SplitView,
text : Text,
tile : Tile
};
export default SwitchComponents
Basically you are telling with SwitchComponents[item.contentType] the position of SwitchComponents that should take, since it's mapped as a component (imported in SwitchComponents) and rendered as <Switcher/> will get a component and will do the trick.
I would be glad to upload the question if it breaks but I hope you get my workaround.
Let me know how it works!
React Loadable has worked brilliantly for me. This is one of those cases when adding a library makes the bundle smaller.
You might also want to defer async loading to when the user has scrolled to the particular element, which this library doesn't do by itself. You can easily mix the original with the InteractionObserver version - react-loadable-visibility. However, make sure you have a good way of handling CLS (Cumulative Layout Shift) or Lighthouse will complain that your website elements are frustrating your users.

React: check if elements in different components overlap

I am looking at many answers of checking if elements overlap, but they are not applicable.
I have a wrapper component that has a header with position fixed and children
const Wrapper = ({ children }) => {
return (
<>
<Header/>
{children}
</>
)
};
export default Wrapper
I need to know when the header is overlapping certain parts in several different pages (children) so as to change the header color
I am trying to detect in the wrapper component, but the elements do not exist or are not accesible to the container
Do I need to pass refs all the way from the wrapper to all the children? Is there an easier way to do this?
Thanks
there are a few approaches i can think of, one being the one you mentioned and passing refs around and doing a bunch of calculations and a few others below.
Hardcoded heights of the pages
This would work basically by having a large switch case in you header file and check the offset scroll position of your scroll target.
getBackgroundColor = (scrollPosition) => {
switch (true) {
case scrollPosition <= $('#page1').height:
return 'red'
case scrollPosition <= $('#page1').height + $('#page2').height:
return 'blue'
case scrollPosition <= $('#page1').height + $('#page2').height + $('page3').height
return 'green'
default:
return 'yellow'
}
}
This has obvious flaws, one being, if the page content is dynamic or changes frequently it may not work, checking height every re-render may cause reflow issues, and this requires knowledge of page IDs on the page. (note: this snippet is just to prove concept, it will need tweeks).
Intersection Observer (Recommended way)
Intersection observer is an awesome API that allows you to observe elements in a performant way and reduces layout thrashing from the alternative ways with constant measurements
heres an example I made with react and intersection observer, https://codesandbox.io/s/relaxed-spence-yrozp. This may not be the best if you have hundreds of targets, but I have not ran any benchmarks on that so not certain. Also it is considered "Experimental" but has good browser support (not IE).
here is the bulk of the codesandbox below just to get an idea.
React.useEffect(() => {
let headerRect = document.getElementById("header").getBoundingClientRect();
let el = document.getElementById("wrapper");
let targets = [...document.getElementsByClassName("block")];
let callback = (entries, observer) => {
entries.forEach(entry => {
let doesOverlap = entry.boundingClientRect.y <= headerRect.bottom;
if (doesOverlap) {
let background = entry.target.style.background;
setColor(background);
}
});
};
let io = new IntersectionObserver(callback, {
root: el,
threshold: [0, 0.1, 0.95, 1]
});
targets.forEach(target => io.observe(target));
return () => {
targets.forEach(target => io.unobserve(target));
};
}, []);
ALso notice this is not the most "React" way to do things since it relys a lot on ids, but you can get around that by passing refs everywhere i have used dom selections, but that may become unwieldy.

How use N views in sample app in react native

I have some doubt in React Native. I read this answer (How to do a multi-page app in react-native?) but I stayed with a little bit doubt:
I use Navigator to many views in React Native App, but how I do to N componentes? For example, if I have 5 different views I have use the before code five times... and n times?
to ellude more on my comment.
instead of this
if (routeId === 'SplashPage') {
return (
<SplashPage
navigator={navigator}/>
);
}
if (routeId === 'LoginPage') {
return (
<LoginPage
navigator={navigator}/>
);
}
just have a hashtable that you use to get the component dynamically.
const Component = VIEW_COMPONENTS[routeid];
so your code would look something like this
const VIEW_COMPONENTS = {
'SplashPage': SplashPage,
'LoginPage': LoginPage
};
renderScene = ( route, navigator ) => {
const ViewComponent = VIEW_COMPONENTS[route.id];
return <ViewComponent navigator={navigator}/>
}
any additional screen would be a single line entry to your lookup table. I have a native app with 40 screens and its very easy to manage like this
to add to this. you can abstract more out of this too. make every view not care about where it is used or what its next view is. Instead make all of that a part of your lookup object. specify a next route that every view can show. you can pass any additional information all of which is configurable and your screens can be reused on multiple flows!

Resources