Understanding how to make React website mobile friendly - reactjs

I've been playing around with react-responsive to understand how to make websites mobile-friendly. Essentially, what I want to do is pass in a value from a function that tests whether a viewing screen is sized like a mobile phone. Practically stolen from the documentation of react-responsive, I have a function in a file called Mobile.js as follows:
const Mobile = () => {
const mobile = useMediaQuery({ query: '(max-width: 1000px)' })
return (
<div>
{mobile && <p>You are sized like a mobile phone</p>}
</div>
);
}
However, what I want to be able to do is pass the boolean "mobile" into other classes in other js files where I can then use different CSS classNames depending on the value of this boolean.
I have 3 specific questions.
How would I return the boolean mobile from the Mobile function to be used?
How would I access this returned boolean?
How would I change the className of a div depending on the value of this boolean?
Pretty new to web development (especially React) and these questions seem super simple and easy to solve, but for some reason, I just can't seem to figure it out by my own online research. Would love direct help and also some resources where I could learn more. Thank you so much!
To me, in a perfect world, the right code would look like this in my mind. Not sure how far off I am, but I was hoping maybe this could be of some guidance as to how I'm thinking.
In the function file,
// FILE Mobile.js
const Mobile = () => {
const mobile = useMediaQuery({ query: '(max-width: 1000px)' })
return (
{ mobile } // how to return a value?
);
}
export default Mobile;
In another file,
// FILE OtherClass.js
import Mobile from './Mobile';
class OtherClass extends Component {
constructor(props) {
super(props);
this.state = { mobile: <Mobile /> } // how to access the returned value?
}
render() {
return (
<div>
{this.state.mobile && <div className="text-mobile">} // how to change the className depending on value?
{!this.state.mobile && <div className="text-desktop">}
blah blah blah blah blah
</div>
</div>
);
}
}

Thanks for asking and welcome to React development !
Here how I can help you
How would I return the boolean mobile from the Mobile function to be
used?
How would I access this returned boolean?
Since you are calling a hook useMediaQuery, you need also a hook to reuse it and returns its value :
function useIsMobile() {
const isMobile = useMediaQuery({ query: '(max-width: 1000px)' });
return isMobile
}
//Then you can share this logic in other components
function Component1(){
const isMobile = useIsMobile()
...
}
function Component2(){
const isMobile = useIsMobile()
...
}
Please note that you can't use hooks inside class components.
How would I change the className of a div depending on the value of
this boolean?
This is straightforward:
function Component(){
const isMobile = useIsMobile()
const className = isMobile ? 'mobile-class' : 'desktop-class'
return <div className={className}>...</div>
}
If you need more complex className logic you can checkout the package classnames which makes it very easy to activate/deactivate classes.

I might be misunderstanding but I think if I were to implement it according to the 3 questions you have it would be something like that:
const MyComponent = () => {
// the point of using hooks is to use it whenever you need it (you "hook it")
const isMobile = useMediaQuery({ query: '(max-width: 1000px)' });
const textStyle = isMobile ? 'text-mobile' : 'text-mobile';
return (
<div className={textStyle}>
enter code here
</div>
)
}
Hope this helps :)
Edit
To reuse this logic you can do a wrapper div component instead:
const MyWrapperComponent = (props) => {
const isMobile = useMediaQuery({ query: '(max-width: 1000px)' });
const textStyle = isMobile ? 'text-mobile' : 'text-mobile';
return (
<div className={textStyle}>
{props.children}
</div>
)
}
// Then you can use it like so:
const HomePage = () => (
<MyWrapperComponent>
write the rest of the code here
</MyWrapperComponent>
)
Children are a kind of props that are native to react, quite basically just means they are whatever you provide between the tags of the component that's receiving them :)
Hope this helps!

Related

How do I parse the HTML from the Lexical editorState without an extra Lexical editor

I have a posts system and when someone submits a post I am saving the editorState as a JSON string in a Postgres database.
Then, when I need to show the HTML, I am using a custom hook which loads an auxiliary editor just to obtain the HTML.
Is this the proper way? Looks to me a bit overengineering 🤔
I don't want to load other editor, and render it too just to obtain the HTML.
Any idea of how to improve this or suggestion for a different approach? 😄
The render post component:
export const PostDetails = () => {
const postSlug = useParam("postSlug", "string")
const [postDetails] = useQuery(getPostPageDetails, { slug: postSlug })
const { html, AuxEditor } = useGetHtmlFromState(postDetails.content as unknown as EditorState)
return (
<>
<AuxEditor />
<Text dangerouslySetInnerHTML={{ __html: html }} />
</>
)
}
The hook to get the HTML useGetHtmlFromState (it uses the same config as the input)
export const useGetHtmlFromState = (state: EditorState) => {
const [html, setHtml] = useState("")
function MyCustomStateToHtmlPlugin() {
const [editor] = useLexicalComposerContext()
editor.update(() => {
const html = $generateHtmlFromNodes(editor, null)
setHtml(html)
})
return null
}
const AuxEditor = () => {
return (
<LexicalComposer
initialConfig={{
namespace: "MyEditor",
onError: console.error,
editorState: state,
theme: exampleTheme,
nodes: [
HeadingNode,
ListNode,
ListItemNode,
QuoteNode,
CodeNode,
CodeHighlightNode,
TableNode,
TableCellNode,
TableRowNode,
AutoLinkNode,
LinkNode,
],
}}
>
<MyCustomStateToHtmlPlugin />
</LexicalComposer>
)
}
return { html, AuxEditor }
}
I think what you have is a creative way to turn SerializedEditorState into HTML. You could experiment with #lexical/headless, but it's essentially the same approach. Alternatively, there are a couple of other ways to solve the underlying problem of displaying saved state.
(1) You can generate the HTML up front and save it to the DB alongside the lexical state. It's a bit duplicative, but it works for the "write once, read many times" use case you've described.
(2) You can use a read-only lexical editor to display the saved state instead of converting it into HTML. You can configure the editor with a different theme if you need more control over the styling of specific elements.
I hope that helps!

How should I update individual items' className onClick in a list in a React functional component?

I'm new to React and I'm stuck trying to get this onClick function to work properly.
I have a component "Row" that contains a dynamic list of divs that it gets from a function and returns them:
export function Row({parentState, setParentState}) {
let divList = getDivList(parentState, setParentState);
return (
<div>
{divList}
</div>
)
}
Say parentState could just be:
[["Name", "info"],
["Name2", "info2"]]
The function returns a list of divs, each with their own className determined based on data in the parentState. Each one needs to be able to update its own info in parentState with an onClick function, which must in turn update the className so that the appearance of the div can change. My code so far seems to update the parentState properly (React Devtools shows the changes, at least when I navigate away from the component and then navigate back, for some reason), but won't update the className until a later event. Right now it looks like this:
export function getDivList(parentState, setParentState) {
//parentState is an array of two-element arrays
const divList = parentState.map((ele, i) => {
let divClass = "class" + ele[1];
return (
<div
key={ele, i}
className={divClass}
onClick={() => {
let newParentState =
JSON.parse(JSON.stringify(parentState);
newParentState[i][1] = "newInfo";
setParentState(newParentState);}}>
{ele[0]}
</div>
)
}
return divList;
}
I have tried to use useEffect, probably wrong, but no luck. How should I do this?
Since your Row component has parentState as a prop, I assume it is a direct child of this parent component that contains parentState. You are trying to access getDivList in Row component without passing it as a prop, it won't work if you write your code this way.
You could use the children prop provided by React that allow you to write a component with an opening and closing tag: <Component>...</Component>. Everything inside will be in the children. For your code it would looks like this :
import React from 'react';
import { render } from 'react-dom';
import './style.css';
const App = () => {
const [parentState, setParentState] = React.useState([
['I am a div', 'bg-red'],
['I am another div', 'bg-red'],
]);
React.useEffect(
() => console.log('render on ParentState changes'),
[parentState]
);
const getDivList = () => {
return parentState.map((ele, i) => {
return (
<div
key={(ele, i)}
className={ele[1]}
onClick={() => {
// Copy of your state with the spread operator (...)
let newParentState = [...parentState];
// We don't know the new value here, I just invented it for the example
newParentState[i][1] = [newParentState[i][1], 'bg-blue'];
setParentState(newParentState);
}}
>
{ele[0]}
</div>
);
});
};
return <Row>{getDivList()}</Row>;
};
const Row = ({ children }) => {
return <>{children}</>;
};
render(<App />, document.getElementById('root'));
And a bit of css for the example :
.bg-red {
background-color: darkred;
color: white;
}
.bg-blue {
background-color:aliceblue;
}
Here is a repro on StackBlitz so you can play with it.
I assumed the shape of the parentState, yu will have to adapt by your needs but it should be something like that.
Now, if your data needs to be shared across multiple components, I highly recommand using a context. Here is my answer to another post where you'll find a simple example on how to implement a context Api.

React: Design pattern that uses a Ref to set style of the parent component

I would like to ask if this is a sensible component design pattern in React.
Let's say I have the following components, App, ContentContainer and WithBlueBackground. The idea is to use a Ref to set a blue background on the ContentContainer with the WithBlueBackground component.
The simplified code would look something like this.
// App.js
export function App() => {
const contentContainerRef = useRef();
return (
<ContentContainer contentContainerRef={contentContainerRef}>
<WithBlueBackground contentContainerRef={contentContainerRef}>
</WithBlueBackground>
</ContentContainer>
)
}
// ContentContainer
export function ContentContainer(props) => {
return (
<div ref={props.contentContainerRef}>
// Some content
</div>
)
}
// WithBlueBackground
export function ContentContainer(props) => {
useEffect(() => {
if (props.containerRef && props.contentContainerRef.current) {
props.contentContainerRef.current.style.backgroundColor = 'blue';
}
}, [props.contentContainerRef])
return <>{ props.children }</>;
}
This way if I want to have a green background in the content container I can create a new component that sets this style without the ContentContainer having to know about this. This increases the composability of the code which is promote in the react docs.
Nevertheless, passing the refs is a bit ugly.
My question is, is this a sensible pattern and if not is there another way to achieve what I am trying to do here.
If it is a direct child, you could just pass an update function around:
// ContentContainer
export function ContentContainer(props) {
const [backgroundColor, setColor] = React.useState("white");
return (
<div style={{ backgroundColor }}>
<ChildComponent setColor={color => setColor(color)}>// Some content</ChildComponent>;
</div>
);
}
// WithBlueBackground
export function ChildComponent(props) {
React.useEffect(() => {
props.setColor("blue");
}, []);
return <>{props.children}</>;
}
If it is deeper nested, you could use the context API. The same principle applies.enter link description here

JSX Element does not have any construct or call signatures

I am trying to add Application Insights in my ReactJS Application. I changed the JS code that is provided on the GitHub Demo to TypeScript.. now I have
class TelemetryProvider extends Component<any, any> {
state = {
initialized: false
};
componentDidMount() {
const { history } = this.props;
const { initialized } = this.state;
const AppInsightsInstrumentationKey = this.props.instrumentationKey;
if (!Boolean(initialized) && Boolean(AppInsightsInstrumentationKey) && Boolean(history)) {
ai.initialize(AppInsightsInstrumentationKey, history);
this.setState({ initialized: true });
}
this.props.after();
}
render() {
const { children } = this.props;
return (
<Fragment>
{children}
</Fragment>
);
}
}
export default withRouter(withAITracking(ai.reactPlugin, TelemetryProvider));
But when I try to import the same component <TelemetryProvider instrumentationKey="INSTRUMENTATION_KEY" after={() => { appInsights = getAppInsights() }}></Telemetry> I get an error Severity Code Description Project File Line Suppression State
(TS) JSX element type 'TelemetryProvider' does not have any construct or call signatures.
I attempted to simply // #ts-ignore, that did not work. How do I go about solving this?
Given the example above, I hit the same issue. I added the following:
let appInsights:any = getAppInsights();
<TelemetryProvider instrumentationKey={yourkeyher} after={() => { appInsights = getAppInsights() }}>after={() => { appInsights = getAppInsights() }}>
Which seem to solve the issue for me, I am now seeing results in Application Insights as expected.
I guess if you want to have the triggers etc on a different Page/Component you may wish to wrap it in your own useHook or just add something like this to the component.
let appInsights:any;
useEffect(() => {
appInsights = getAppInsights();
}, [getAppInsights])
function trackEvent() {
appInsights.trackEvent({ name: 'React - Home Page some event' });
}
Not the best answer, but it's moved me forward. Would be nice to see a simple hooks version in typescript.
Really hope it helps someone or if they have a cleaner answer.

How to implement GTM with reactjs

I am trying to implement GTM with reactjs. I have used react-google-tag-manager but it did not solve the purpose.
Somehow, the data layer needs to be in a particular format and also the needs to be right below the tag, but it is only one of them that i can achieve at a time.
I tried placing the code directly in template.html and call the function from the component i wanted, but that didn't work.
import React from 'react';
import gtmParts from 'react-google-tag-manager';
class GoogleTagManager extends React.Component {
componentDidMount() {
const dataLayerName = this.props.dataLayerName || 'dataLayer';
const scriptId = this.props.scriptId || 'react-google-tag-manager-gtm';
if (!window[dataLayerName]) {
const gtmScriptNode = document.getElementById(scriptId);
eval(gtmScriptNode.textContent);
}
}
render() {
const gtm = gtmParts({
id: this.props.gtmId,
sourcegroup: this.props.gtmGroupname,
sourceid:this.props.gtmSource,
age:this.props.age,
mtongue:this.props.gtmMtongue,
city:this.props.city,
});
return (
<div>
<div>{gtm.noScriptAsReact()}</div>
<div id={this.props.scriptId || 'react-google-tag-manager-gtm'}>
{gtm.scriptAsReact()}
</div>
</div>
);
}
}
export default GoogleTagManager;
I am pushing parameters in DataLayer and on checking on google tag assistant addon, whole the datalyer is empty.
I had this issue yesterday and to solve it, I had to put all the properties that I'm trying to record under the additionalEvents property. Something like this:
const gtm = gtmParts({
id: this.props.gtmId,
additionalEvents: {
sourcegroup: this.props.gtmGroupname,
sourceid:this.props.gtmSource,
age:this.props.age,
mtongue:this.props.gtmMtongue,
city:this.props.city
}
})
And also avoid using eval() since this is a dangerous pratique. Update your code like this:
if (!window[dataLayerName]) {
const script = document.createElement("script")
const gtmScriptNode = document.getElementById(scriptId)
const scriptText = document.createTextNode(gtmScriptNode.textContent)
script.appendChild(scriptText)
document.head.appendChild(script)
}

Resources