return component with hook in react? - javascript

I have 2 components. I want to keep component A clean, hence I don't wish to use useState in it, I wish to use useState in component B, but since the component B isn't a hook (because it return the jsx as well), how can I call stateHandler in component A?
const ComponentA = () => {
return (
<div>
<ComponentB />
<button onClick={()=>{
//what to do here to control the state of component B?
}}>show component B</button>
</div>
)
}
const ComponentB = () => {
//I don't want to move this setShowBlock to index.js for clean code purpose
const [showBlock, setShowBlock] = useState(false)
return (
<div>
{showBlock && <div>component B</div>}
</div>
)
}
https://stackblitz.com/edit/react-ggtt87?file=index.js

you can use context as central store,
in the context you create the useState hook with showBlock and setShowBlock and then you can change it from component A
const ComponentA = () => {
const {setShowBlock} = useContext(MyContext)
return (
<div>
<ComponentB />
<button onClick={()=>{
here call setShowBlock and change it
}}>show component B</button>
</div>
)
}

Related

How to pass the onClick() event to a child component from a button in the parent component in React?

Let's say I have two components Component1 and Component2. I have a button in Component1.
const Component1 = () => {
return(
<div>
<button onClick={()=>someExportFunc()}>Export</button>
<Component2 />
</div>
)
}
My Component2.jsx is something like this.
import {ChartComponent} from '#xyz/charts';
const Component2 = () => {
let chartInstance = new ChartComponent();
return (
<ChartComponent
id='chart1'
ref={chart = chartInstance => chart}
>
</ChartComponent>
)
}
Now the function someExportFunc() is defined inside the {ChartComponent} being imported in Component2.jsx.
Had I used the button inside Component2, it would have worked simply. As in,
import {ChartComponent} from '#xyz/charts'
const Component2 = () => {
let chartInstance = new ChartComponent();
return (
<button onClick={()=>someExportFunc()}>Export</button>
<ChartComponent
id='chart1'
ref={chart = chartInstance => chart}
>
</ChartComponent>
)
}
But how can I make it work as a button in Component1?
I am guessing it has something to do with defining state of the button and then passing the state down, but I don't exactly understand how the state of a button works. onClick() should be some instantaneous boolean, isn't it? By instantaneous, I mean it would change as soon as the 'click' is over.
Edit1: Okay, so the someExportFunc() is a library function defined in my import. It makes use of the chartInstance created in Component2. It's actual use case is
<button
value='export'
onClick={() => chartInstance.exportModule.export('PNG',
`image`)}>
Export
</button>
So basically I want it so that when I click the button within Component1, it sends the onClick event to my ChartComponent in Component2.
we need to pass the function as a prop to the second component.
FirstComponent
<SecondComponent { ...data } onClick={ () => manageClick() } />
in the second Component use the onClick prop on click.
SecondComponent
<button onClick={onClick}>Click</button>
React uses one-way data binding, meaning parent to child in this case.
If Component 2 is a child of Component 1, define the function in Component 1 and pass the function and Chart Component as props and children respectively.
Parent:
const Component1 = () => {
const someFunc = () => {...}
return <Component2 someFunc={someFunc}> <ChartComponent/> </Component2>
}
Child:
const Component2 = (props) => {
props.someFunc();
return <div> {props.children} </div>
}

How can I make a component render onClick in a React functional component?

I'm a bit surprised I'm having trouble finding this online, but I can't seem to find an example of how to do this in a React functional component. I have a React component that I would like to render when I click a button. Right now the function fires and I can see my console.log firing, however the component isn't rendering. My first guess was that it won't render because React doesn't know to update the view, however I added boolean via useState and it still won't render. What am I doing wrong?
Below is the relevant code. How can I get the component in addSection to render?
const FormGroup = ({index}) => {
const [additionalSection, setAdditionalSection] = useState(false);
const addSection = form => {
setAdditionalSection(true);
console.log('form', form);
return additionalSection && (
<div key={form.prop}>
<p>This should render</p>
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
</div>
);
};
...
return (
...
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)}
>
span className="button--small">{form.button}</span>
</LinkButton>
</FormAdd>
);
You should change your state (or a prop in your useEffect dependency array in case you had one) in order to force a rerender. In this case:
setAdditionalSection(prevState=>!prevState);
A state change like the one you are calling, will trigger a re-render.
But all html to be rendered must be included in the functional components return statement.
The elements you want to render can be conditionally rendered like this:
const FormGroup = ({index}) => {
const [additionalSection, setAdditionalSection] = useState(false);
const addSection = form => {
setAdditionalSection(true);
console.log('form', form);
};
...
return (
...
<FormAdd>
<LinkButton
type="button"
onClick={() => addSection(form)}
>
<span className="button--small">{form.button}</span>
</LinkButton>
{additionalSection &&
<div key={form.prop}>
<p>This should render</p>
<AdditiveSection
form={form}
register={register}
errors={errors}
/>
</div>
}
</FormAdd>
);

Why does React re-renders children when props does not change?

Consider the following example:
import "./styles.css";
import React from "react";
function Children(props) {
const counter = props.counter2;
console.log("re-rendering children");
return (
<div className="App">
<p>counter2 value = {counter} </p>
</div>
);
}
export default function App() {
const [counter, setCounter] = React.useState(0);
const [counter2] = React.useState(0);
return (
<div className="App">
<p>counter value = {counter} </p>
<button onClick={(e) => setCounter((c) => c + 1)}>increment</button>
<Children counter2={counter2} />
</div>
);
}
The Children component does not depend on the parent state counter, but when I click the button I can see that re-rendering log printed every time I click. If I am not mistaken then it means the Children component is re-rendered. My question is why? It should not re-render as the props did not change.
code sandbox: https://codesandbox.io/s/modern-dawn-xj9b8
Because of the lifecycle of React, when you are using a functional component, it is also considered stateless and will re-render every time there is an update in its parent component, whether there is a change in props passing into the child.
There are two ways you can do to change this behaviour,
use class component and decide whether to update it using shouldComponentUpdate
or the recommended way using React.memo,
import "./styles.css";
import React from "react";
function Children(props) {
const counter = props.counter2;
console.log("re-rendering children");
return (
<div className="App">
<p>counter2 value = {counter} </p>
</div>
);
}
const MemoChild = React.memo(Children);
export default function App() {
const [counter, setCounter] = React.useState(0);
const [counter2] = React.useState(0);
return (
<div className="App">
<p>counter value = {counter} </p>
<button onClick={(e) => setCounter((c) => c + 1)}>increment</button>
<MemoChild counter2={counter2} />
{/* <Children counter2={counter2} /> */}
</div>
);
}
More on React.memo in the docs
If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
this answer is true that you can use React.memo but before start using, check some articles to figure out when it's valuable to use it.
using it inappropriate could damage your performance.

Can't pass useState() 'set' function to grand child

I'm having issues trying to get my useState variable to work. I create the state in my grandparent then pass it into my parent. Here's a simplified version of my code:
export function Grandparent(){
return(
<div>
const [selectedID, setSelectedID] = useState("0")
<Parent setSelectedID2={setSelectedID} .../> //(elipses just mean that I'm passing other params too)
<div />
)}
Parent:
const Parent = ({setSelectedID2 ...}) => {
return(
<div>
{setSelectedID2("5")} //works
<Child setSelectedID3={setSelectedID2} />
</div>
)
}
From the parent I can use 'setSelectedID2' like a function and can change the state. However, when I try to use it in the child component below I get an error stating 'setSelectedID3' is not a function. I'm pretty new to react so I'm not sure if I'm completely missing something. Why can I use the 'set' function in parent but not child when they're getting passed the same way?
Child:
const Child = ({setSelectedID3 ...}) => {
return(
<div >
{setSelectedID3("10")} //results in error
</div>
);
};
In React you make your calculations within the components/functions (it's the js part) and then what you return from them is JSX (it's the html part).
export function Grandparent(){
const [selectedID, setSelectedID] = useState("0");
return(
<div>
<Parent setSelectedID2={setSelectedID} .../> //(elipses just mean that I'm passing other params too)
<div />
)}
You can also use (but not define!) some js variables in JSX, as long as they are "renderable" by JSX (they are not Objects - look for React console warnings).
That's your React.101 :)
Here's a working example with everything you have listed here. Props are passed and the function is called in each.
You don't need to name your props 1,2,3.., they are scoped to the function so it's fine if they are the same.
I moved useState and function calls above the return statement, because that's where that logic should go in a component. The jsx is only used for logic dealing with your display/output.
https://codesandbox.io/s/stupefied-tree-uiqw5?file=/src/App.js
Also, I created a working example with a onClick since that's what you will be doing.
https://codesandbox.io/s/compassionate-violet-dt897?file=/src/App.js
import React, { useState } from "react";
export default function App() {
return <Grandparent />;
}
const Grandparent = () => {
const [selectedID, setSelectedID] = useState("0");
return (
<div>
{selectedID}
<Parent setSelectedID={setSelectedID} selectedID={selectedID} />
</div>
);
};
const Parent = ({ selectedID, setSelectedID }) => {
setSelectedID("5");
return (
<div>
{selectedID}
<Child setSelectedID={setSelectedID} selectedID={selectedID} />
</div>
);
};
const Child = ({ selectedID, setSelectedID }) => {
setSelectedID("10");
return <div>{selectedID}</div>;
};
output
10
10
10
const [selectedID, setSelectedID] = useState("0")
should be outside return

export Hooks in React for Nested Components?

I'm exporting hooks with nested components so that the parent can toggle state of a child. How can I make this toggle work with hooks instead of classic classes or old school functions?
Child Component
export let visible;
export let setVisible = () => {};
export const ToggleSwitch = () => {
const [visible, setVisibile] = useState(false);
return visible && (
<MyComponent />
)
}
Parent
import * as ToggleSwitch from "ToggleSwitch";
export const Parent: React.FC<props> = (props) => {
return (
<div>
<button onClick={() => ToggleSwitch.setVisible(true)} />
</div>
)
}
Error: Linter says [setVisible] is unused variable in the child... (but required in the parent)
You can move visible state to parent like this:
const Child = ({ visible }) => {
return visible && <h2>Child</h2>;
};
const Parent = () => {
const [visible, setVisible] = React.useState(false);
return (
<div>
<h1>Parent</h1>
<Child visible={visible} />
<button onClick={() => setVisible(visible => !visible)}>
Toggle
</button>
</div>
);
};
If you have many child-components you should make more complex logic in setVisible. Put object to useState where properties of that object will be all names(Ids) of child-components
as you know React is one-way data binding so if you wanna pass any props or state you have only one way to do that by passing it from parent to child component and if the logic becomes bigger you have to make it as a global state by using state management library or context API with react hooks use reducer and use effect.

Categories