Get state variable from child component using React 16.8^ coding style - reactjs

This question has already been asked but i cannot find an answer using hooks.
I need to read a state variable in the page that calls a component.
MyPage.js
import React from "react"
import MyComponent from "../components/MyComponent"
export default function MyPage() {
console.log("Read stateVariable value here " + stateVariable) // produce error
return (
<MyComponent />
)
}
MyComponent.js
import React, { useState } from "react"
import ExternalModule from "ExternalModule"
export default function MyComponent() {
const [stateVariable, setStateVariable] = useState(0))
return (
<div
onClick={setStateVariable(stateVariable + 1)}
>CLICK HERE
<ExternalModule
stateVariable={stateVariable}
/>
</div>
)
How to console.log stateVariable in MyPage?

When you have multiple components that need to access a piece of state, you want that state to be owned by the highest-up component in the tree and passed down to the rest.
Move the useState call up to MyPage and pass both stateVariable and setStateVariable down to MyComponent as props.
export default function MyPage() {
const [stateVariable, setStateVariable] = useState(0)
console.log("Read stateVariable value here " + stateVariable)
return (
<MyComponent
stateVariable={stateVariable}
setStateVariable={setStateVariable}
/>
)
}
export default function MyComponent({ stateVariable, setStateVariable]) {
return (
<div
onClick={setStateVariable(stateVariable + 1)}
>CLICK HERE
<ExternalModule
stateVariable={stateVariable}
/>
</div>
)
}

Related

How to prevent component from being re-rendered unnecessarily

I'll start with the code. I have a stateless functional component that resembles this
export const Edit Topic = (_title, _text) {
const [title, setTitle] = useState(_title)
const [text, setText] = useState(_text)
return (
<>
<InputText props={{ fieldName:"Title:", value:title, setValue:setTitle, placeHolder:"Topic Title"}}/>
<InputTextArea props={{ fieldName:"Markdown Text:", text, setText }}/>
<PreviewBox text={text}/>
</>
)
}
I have PreviewBox when it's on, page rendering takes a bit longer because text can be quite long. PreviewBox needs to re-render each time I change text in InputTextArea and that's fine.
The problem I'm having is when I change the value of title it's also updating <PreviewBox/> which is undesired.
How can I make sure that <PreviewBox/> only updates when text changes and not when title changes?
The reason why I believe the re-rendering is occuring is because if I toggle off PreviewBox, there's no lag in when updating title but when PreviewBox is visible the updating the title lags.
import style from "../styles/CreateTopic.module.css"
import { Component } from "react"
import Markdown from "./Markdown";
export class PreviewBox extends Component {
constructor(props) {
super(props)
this.state = {
isShow: true
}
}
toggleShow = () => {
console.log("begin isShow", this.state)
this.setState(state => ({ isShow: !state.isShow}))
}
render() {
return (
<>
<div className={style.wrptoggle}>
<button className={style.btn} onClick={this.toggleShow}>Preview</button>
</div>
{this.state.isShow ?
<div className={style.wrppreviewbox}>
<div className={style.previewbox}>
<Markdown text={this.props.text}/>
</div>
</div>
: null}
</>
)
}
}
Since the above also contains <Markdown/> here's that component:
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import ReactMarkdown from "react-markdown";
import "katex/dist/katex.min.css";
const Markdown = ({text}) => {
return (
<div>
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
children={text}
/>
</div>
);
}
export default Markdown;
I don't see any complexity in PreviewBox that would cause any rendering delay so I might assume it's the Markdown component that may take some time "working" when it's rerendered since you say "toggle off PreviewBox, there's no lag in when updating title".
Solution
You can use the memo Higher Order Component to decorate the Markdown component and provide a custom areEqual props compare function.
import { memo } from 'react';
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import ReactMarkdown from "react-markdown";
import "katex/dist/katex.min.css";
const Markdown = ({ text }) => {
return (
<div>
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
children={text}
/>
</div>
);
};
export default memo(Markdown);
By default it will only shallowly compare complex objects in the props
object. If you want control over the comparison, you can also provide
a custom comparison function as the second argument.
const areEqual = (prevProps, nextProps) => {
return prevProps.text === nextProps.text;
};
export default memo(Markdown, areEqual);

Map through two arrays of components and strings and render in one component

I have two arrays that I want to map through:
const social = ["Snapchat", "TikTok", "Dribbble", "Discord", "Facebook"];
const socialIcons = [<SnapchatIcon />, <DribbbleIcon />];
The socialIcons array are all components
How can I send both values as props into my DummyRectangle component? Here is my current code:
{social.map((s, index) => (
<div className="dummy_buttonsWrapper">
<DummRectangle social={s} socialIcons={i} />
</div>
))}
And here is DummyRectangle component:
function DummRectangle({ social, socialIcons }) {
// console.log("---->", socialIcons);
return (
<div>
<p>{social}</p>
{<socialIcon/>} // render social icon component
</div>
);
}
To do so, you don't need to wrap tags around your socialIcon in your DummRectangle. Also, it doesn't seem that you are passing the socialIcon component at all. If I were you, I would do something like this:
The following two are the components as an example that you would like to render (in your case - socialIcons)
// Comp1.js
import React from "react";
const Comp1 = () => <div>actual Comp1</div>;
export default Comp1;
// Comp2.js
import React from "react";
const Comp2 = () => <div>actual Comp2</div>;
export default Comp2;
Now, in your main Parent component, you would simply get the current component of the componentName (in your case - social) by accessing your component's array with an index. Then, you would pass this currentComponent as props to your Child component where you want to render it.
// App.js
import React from "react";
import Comp1 from "./Comp1";
import Comp2 from "./Comp2";
import DummyComponent from "./DummyComponent";
export default function App() {
const componentNames = ["Comp1", "Comp2"];
const components = [<Comp1 />, <Comp2 />];
return (
<div className="App">
{componentNames.map((name, index) => {
const currentComponent = components[index];
return (
<div>
<DummyComponent componentName={name} component={currentComponent} />
</div>
);
})}
</div>
);
}
In your Child component, you can simply render it by enclosing it into the brackets - no need to add tags. React will do all the rendering for you. In your case it would be { socialIcon }
// DummyComponent.js
import React from "react";
const DummyComponent = ({ componentName, component }) => {
return (
<div>
<p>{componentName}</p>
{component}
</div>
);
};
export default DummyComponent;
Link to Codesandbox with the above code for reference: click here

Functional Component's props in Higher Order Component

I am trying to understand passing the functional component's props to the returned functional component
**CODE ------------------------------------------------
App Component:
import React from 'react';
import ClickCounter from './ClickCounter';
const App = () => {
return (
<div className="App">
<ClickCounter firstName="John" lastName="Doe"/>
</div>
);
}
export default App;
ClickCounter Component:
import React from 'react'
import withCounter from './withCounter'
const ClickCounter = (props) => {
const { count, incrementCount, name } = props
return (
<div className="click-counter">
<button onClick={incrementCount}>Click Button</button>
<h2>{name}</h2>
<h1>{count}</h1>
</div>
)
}
export default withCounter(ClickCounter, 10)
withCounter Component (HOC)
import React, { useState } from 'react'
const withCounter = (WrappedComponent, incrementNumber) => {
return props => { // ** A **
console.log('props ---- ', props)
const [count, setCount] = useState(0)
return (
<WrappedComponent. // ** B **
count={count}
incrementCount={() => setCount(count + incrementNumber)}
{...props}
/>
)
}
}
export default withCounter
From my understanding, the withCounter return a functional component (A) that use useState Hook and and it return another component (B) which access the state through closure.
** QUESTION -------------------------------------------
My question is why the props in A is firstName="John" lastName="Doe", is it a React thing or Javascript's thing.
Does React pass the parameter's props (i.e wrapped component's props) to the returned functional component? or it is a Javascript's thing?
i read it in another post saying it is related to curry, but i cannot see it is related to curry, below is the post i read
HoC with React Hooks
const useSample = WrappedComponent => props => { // curry
const sampleCtx = useContext(SampleCtx);
return (
<WrappedComponent
{...props}
value={sampleCtx.value}
setValue={sampleCtx.setValue}
/>
);
};
Thank you!!
My question is why the props in A is firstName="John" lastName="Doe", is it a React thing or Javascript's thing.
This is because that's the props you passed to your Counter. Attributes on a component are props passed to functional components. This is a React thing.
<ClickCounter firstName="John" lastName="Doe"/>
When you put the component ClickCounter in your tree, it's actually just withCounter(ClickCounter, 10) being called, since that's your default export.
withCounter is a function that returns another function (curry) that takes props and returns a component. This is the setup for a functional component.
React runs this function and passes the props firstName="John" lastName="Doe" to that function. Then, those props are added to your WrappedComponent via the spread operator {...props}.

How to add {props.children} to a React component

i have many components which have {props.children} deeply nested inside.
considery DRY principle is there a way to add this using some React pattern.
example
let's say i have two components,
Comp1.js
import React from "react";
const Comp1 = props => {
return (
<div>
<h1>{props.children}</h1>
</div>
);
};
export default Comp1;
Comp2.js
import React from "react";
const Comp2 = props => {
return (
<div>
<div>
<h1>{props.children}</h1>
</div>
</div>
);
};
export default Comp2;
if you see above code we have both Comp1 and Comp2 have line of code {props.children} repeated inside.
what i want now is some function which will add this line of code, something like below,
const addPropsChildrenToComp = (Comp)=>{
return(
(props)=>{
///do somehting here
}
)
}
const Comp1 = props => {
return (
<div>
<h1></h1>
</div>
);
};
Comp1WithPropsChildren = addPropsChildrenToComp(Comp1)
using HOC doesn't work because, in HOC we never modify passed component.
anyway to aceve this.?
to get more idea of my problem see this demo: https://codesandbox.io/s/trusting-http-pd1yu
in there i woul like to see CompWithPropsChildren component render props.children inside it.
I think I see what you're trying to get to, and you can accomplish this just using another component.
import React from "react";
import ChildComp from "./ChildComp";
const Comp1 = props => {
return (
<div>
<ChildComp {...props} />
</div>
);
};
export default Comp1;
import React from "react";
const ChildComp = props => {
return <h1>{props.children}</h1>
}
Assuming your ChildComp has some complex logic you don't want to duplicate, this will make it reusable for you.

Invalid hook call. Hooks can only be called inside of the body of a function component when using Hooks

I am not able to use React hooks. I have 4 components:
ComponentA
componentC
componentE
componentF
I need to pass value to componentF directly from componentA without having to pass from componentC and componentE. All components are in a single tree.
// componentA
import React from 'react';
import './App.css';
import ComponentC from "./components/ComponentC";
export const UserContext = React.useContext();
function App() {
return (
<div className="App">
<UserContext.Provider>
<ComponentC value={'My message'}/>
</UserContext.Provider>
</div>
);
}
export default componentA;
// componentF
import React from 'react';
import UserContext from '../App';
function ComponentF() {
return (
<div>
<UserContext.Consumer>
{
user => {
return (
<div>you are {user}</div>
)
}
}
</UserContext.Consumer>
</div>
)
}
export default ComponentF;
It is giving an error when I am trying to use context:
Invalid hook call. Hooks can only be called inside of the body of a function component.
The mistake is that you are using React.useContext()
You should be doing:
export const UserContext = React.createContext();
To pass to the child components of Component A, you can do it like this:
<UserContext.Provider value={100}>
<ComponentC .../>
</UserContext.Provider>
And instead of using UserContext.Consumer, you can get the value using React.useContext inside the component body of ComponentF.
// componentF
import React from 'react';
import {UserContext} from '../App';
function ComponentF() {
const value = React.useContext(UserContext);
return (
<div>
....
{value} // which will be equal to 100
</div>
)
}
export default ComponentF;

Resources