Change the state of another component using functionnal components in React - reactjs

I have a button in my header that has to switch the state of the lateral menu of the page (to know if it has to be shown or not). I found out on Internet how to do so using Class Components, but not with Functional Components, do you have any idea on how to achieve that ?
Here's a simplification of my actual code in order to reproduce my issue (I removed all useless code).
App.js :
function App() {
return (
<div className="App">
<Header />
<div>
<LateralMenu />
</div>
</div>
);
}
The Header component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
function changeLateralMenu() {
setLateralIsOpen(!lateralIsOpen);
}
return (
<header>
<div onClick={ changeLateralMenu }>
</header>
);
}
And the LateralMenu component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<section>
{ lateralIsOpen ? "open" : "closed" }
</section>
);
}
I tried (but maybe not correctly) to declare the lateralIsOpen State in the App component and sending it through props to my children componenents (Header & LateralMenu).
I also tried looking at this question (and a few others) which is pretty similar, but don't see how I can apply it in my case because (as I understand) he uses a button in the parent component, that changes a state in the parent component, and then send it to the children through props... Where in my case, the button to switch it is already in a child.

I'd suggest you to move the state out of the LateralMenu to the parent (App) and then just pass the toggle function to the Header to toggle it.
export default function App() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<div className="App">
<Header toggleLateral={() => setLateralIsOpen((prev) => !prev)} />
<div>
<LateralMenu isOpen={lateralIsOpen} />
</div>
</div>
);
}
function Header({ toggleLateral }) {
function changeLateralMenu() {
toggleLateral();
}
return (
<header>
<div onClick={changeLateralMenu}>click</div>
</header>
);
}
function LateralMenu({ isOpen }) {
return <section>lateral is {isOpen ? 'open' : 'closed'}</section>;
}
Codesandbox: https://codesandbox.io/s/eager-heyrovsky-z75njd

Related

Why Won't This Render as JSX?

I have a string variable, with this HTML code like so:
this.state.contents:
<p>This Handbook is designed to provide you with information about your employment at {this.props.clientName} and its affiliates and subsidiaries (referred to throughout this Handbook as “{this.props.clientName}” or the “Company”).</p>
I pass it to a child component like this:
<Policy key={filteredPolicy.id} id={filteredPolicy.id} contents={filteredPolicy.contents} clientName={this.state.client}/>
The child component is this:
<Card.Body className="content" >{this.props.contents}</Card.Body>
But in the component, it renders as the string and just shows all the HTML tags. How do I get it to actually render correctly as HTML code? A second issue is the variables of {this.props.clientName} also do not show up... it just literally renders {this.props.clientName} as a string. It all works correctly if I just do it as HTML. But when I try to do it this way, no dice.
While you can still use dangerouslySetInnerHTML to achieve it like this, it's not recommended
function Child({ data }) {
return <div dangerouslySetInnerHTML={{__html: data}}></div>;
}
const test = "test-data";
export default function App() {
const [data] = useState(`<div>${test}</div>`); // note the ${} (template literal) here instead of {}, because this is a string, not React component
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child data={data} />
</div>
);
}
Live example
Define a renderData function inside Parent component to render your html, pass it to Child as a prop, and call that renderData function here. You can use your state, props inside that renderData function as you want.
So the above example can be rewrite like this
function Child({ renderData }) {
return <div>{renderData()}</div>;
}
const test = "test-data";
export default function App() {
const renderData = () => {
return (
<div>{test}</div>
)
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child renderData={renderData} />
</div>
);
}
Live example

Div content is mapping different components

I want to ask how could I implement in reactjs the next thing.
Imagine a page with 2 buttons and a div. When I press one button the content of the div would be a list of components mapped. When I press the other button the div content would be another list of components ( different component type as well ).
So more or less, the effect that results from this is a menu inside a page that loads different things in a div.
I am also willing to hear other options of implementing this, but mostly i'm here to ask you for a short example of how could I implement this thing.
import { useState } from "react";
import "./styles.css";
const Component1 = () =>{
return(
<div>
<h1>Component 1</h1>
</div>
)
}
const Component2 = () =>{
return(
<div>
<h1>Component 2</h1>
</div>
)
}
export default function App() {
const [component, setComponent] = useState("COMPONENT_1");
const renderComponent = () =>{
switch(component){
case "COMPONENT_1":
return <Component1/>
case "COMPONENT_2":
return <Component2/>
default:
return <div/>
}
}
return (
<div className="App">
<div>
{renderComponent()}
</div>
<button onClick={()=>setComponent("COMPONENT_1")}>Render component 1</button>
<button onClick={()=>setComponent("COMPONENT_2")}>Render component 2</button>
</div>
);
}
you can replace Component1 and Component2 with whatever you want.
Or
You can use a library like React Router https://reactrouter.com/
let me know if there is anything else you want me to add.

How a child component can get several props/data from two separate parent components

In one component, why can't I receive two/multiple props from separate components?
I'm getting this error:
TypeError: props.name is not a function.
What am I doing wrong?
Here is how I tried to do it -
function Welcome() {
const [searchString, setString] = useState('');
return (
<div className='App'>
<Header name={searchString} />
</div>
);
}
function Home() {
const [numbers,setNumbers] = useState('');
retrun(
<Header moneyAmount={numbers} />
)
}
function Header(props) {
return(
<div>
{props.name}
{props.moneyAmount}
</div>
)
}
All tries this way or as function Header({name, moneyAmount}){}
field.
Is there's a way with createContext or useContext ?
I tried with useContext but I think it's collide with the props, still gives an error.
You've 3 components: Welcome, Home and Header
Welcome is using the Header component and passing it a name prop, so it will only render the name you passed (which you gave the wrong variable, should be name={searchString} instead of name={setString}).
Home is also using the Header component and passing it a moneyAmount prop,
so it will only render the moneyAmount variable.
Welcome and Home have no relation, so the Header component in each one of them dosen't know about the props passed from the other. If you want to pass multiple variables, you must pass directly to the component like so:
function Home(props) {
const [numbers,setNumbers] = useState('');
return (
<Header moneyAmount={numbers} name={prop.searchString} />
)
}
If you want to pass data to a child from 2 different components, you do it like so:
function Welcome() {
const [searchString, setString] = useState('');
return (
<div className='App'>
<Home name={searchString} />
</div>
);
}
function Home(props) {
const [numbers,setNumbers] = useState('');
return (
<Header moneyAmount={numbers} name={props.name} />
)
}
function Header(props) {
return(
<div>
{props.name}
{props.moneyAmount}
</div>
)
}

Creating a parent 'workspace' component in ReactJS

Using ReactJS, I am trying to create a common workspace component that will have toolbar buttons and a navigation menu. The idea I have is to re-use this component to wrap all other dynamic components that I render in the app.
Currently, I've created a Toolbar and MenuBar components that I then add to each component in the app as such:
<Toolbar/>
<MenuBar/>
<Vendors/>
This does not feel right, since my aim is to have just one component which would be something like:
<Workspace>
<Vendor/>
</Workspace>
However, I am not sure of how to achieve this and whether this is the right approach.
As to whether or not it is the right approach is subjective, but I can provide insight into one way to make a "wrapper" type component:
// Your workspace wrapper component
class Workspace {
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
Toolbar goes here
</div>
<div className="workspace__nav">
Navgoes here
</div>
<div className="workspace__content">
{this.props.children}
</div>
</div>
)
}
}
// Using the component to define another one
class MyComponent {
render() {
return (
<Workspace>
This is my workspace content.
</Workspace>
)
}
}
You can also look at HOC's or Higher Order Components to wrap things.
React offer two traditional ways to make your component re useable
1- High-order Components
you can separate the logic in withWorkspace and then give it a component to apply that logic into it.
function withWorkSpace(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const Component = () => {
const Content = withWorkSpace(<SomeOtherComponent />)
return <Content />
}
2- Render Props
or you can use function props then give the parent state as arguments, just in case you need the parent state in child component.
const Workspace = () => {
state = {}
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
{this.props.renderTollbar(this.state)}
</div>
<div className="workspace__nav">
{this.props.renderNavigation(this.state)}
</div>
<div className="workspace__content">
{this.props.children(this.state)}
</div>
</div>
)
}
}
const Toolbar = (props) => {
return <div>Toolbar</div>
}
const Navigation = (props) => {
return <div>Toolbar</div>
}
class Component = () => {
return (
<Workspace
renderNavigation={(WorkspaceState) => <Navigation WorkspaceState={WorkspaceState} />}
renderTollbar={(WorkspaceState) => <Toolbar {...WorkspaceState} />}
>
{(WorkspaceState) => <SomeComponentForContent />}
</Workspace>
)
}

How to trigger a function from one component to another component in React.js?

I'am creating React.js Weather project. Currently working on toggle switch which converts celcius to fahrenheit. The celcius count is created in one component whereas toggle button is created in another component. When the toggle button is clicked it must trigger the count and display it. It works fine when both are created in one component, but, I want to trigger the function from another component. How could I do it? Below is the code for reference
CelToFahr.js (Here the count is displayed)
import React, { Component } from 'react'
import CountUp from 'react-countup';
class CeltoFahr extends Component {
state = {
celOn: true
}
render() {
return (
<React.Fragment>
{/* Code for celcius to farenheit */}
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<CountUp
start={!this.state.celOn ? this.props.temp.cel : this.props.temp.fahr}
end={this.state.celOn ? this.props.temp.cel : this.props.temp.fahr}
duration={2}
>
{({ countUpRef, start}) => (
<h1 ref={countUpRef}></h1>
)}
</CountUp>
</div>
</div>
</div>
</div>
{/*End of Code for celcius to farenheit */}
</React.Fragment>
)
}
}
export default CeltoFahr
CelToFahrBtn (Here the toggle button is created)
import React, { Component } from 'react'
import CelToFahr from './CeltoFahr'
class CelToFahrBtn extends Component {
state = {
celOn: true
}
switchCel = () => {
this.setState({ celOn: !this.state.celOn })
}
render = (props) => {
return (
<div className="button" style={{display: 'inline-block'}}>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<div onClick={this.switchCel} className="CelSwitchWrap">
<div className={"CelSwitch" + (this.state.celOn ? "" : " transition")}>
<h3>C°</h3>
<h3>F°</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CelToFahrBtn
Here when I click on switchCel it must trigger the celcius to fahrenheit value and vice-versa. How to do it? Any suggestions highly appreciated. Thanks in advance
I would have the celToFahr be the parent component of the celToFahrBtn and then pass the function you want to invoke via props
<CellToFahrBtn callback={yourfunction}/>
What else could you do is having a common parent for these to components where you would again do the execution via props and callbacks
The 3rd option would be having a global state which would carry the function like Redux or Reacts own Context. There again you would get the desired function via props and you would execute it whenever you like. This is the best option if your components are completely separated in both the UI and in source hierarchically, but I don't think this is the case in this case.
https://reactjs.org/docs/context.html
These are pretty much all the options you have
To achieve this you'd need to lift your state up and then pass the state and handlers to the needed components as props.
CeltoFahr & CelToFahrBtn would then become stateless components and would rely on the props that are passed down from TemperatureController
class TemperatureController extends Component {
state = {
celOn: true
}
switchCel = () => {
this.setState({ celOn: !this.state.celOn })
}
render () {
return (
<React.Fragment>
<CeltoFahr celOn={this.state.celOn} switchCel={this.state.switchCel} />
<CelToFahrBtn celOn={this.state.celOn} switchCel={this.state.switchCel}/>
</React.Fragment>
)
}
}
It's probably better explained on the React Docs https://reactjs.org/docs/lifting-state-up.html
See this more simplified example:
import React, {useState} from 'react';
const Display = ({}) => {
const [count, setCount] = useState(0);
return <div>
<span>{count}</span>
<Button countUp={() => setCount(count +1)}></Button>
</div>
}
const Button = ({countUp}) => {
return <button>Count up</button>
}
It's always possible, to just pass down functions from parent components. See Extracting Components for more information.
It's also pretty well described in the "Thinking in React" guidline. Specifically Part 4 and Part 5.
In React you should always try to keep components as dumb as possible. I always start with a functional component instead of a class component (read here why you should).
So therefore I'd turn the button into a function:
import React from 'react';
import CelToFahr from './CeltoFahr';
function CelToFahrBtn(props) {
return (
<div className="button" style={{ display: 'inline-block' }}>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<div onClick={() => props.switchCel()} className="CelSwitchWrap">
<div
className={'CelSwitch' + (props.celOn ? '' : ' transition')}
>
<h3>C°</h3>
<h3>F°</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default CelToFahrBtn;
And you should put the logic in the parent component:
import React, { Component } from 'react';
import CountUp from 'react-countup';
import CelToFahrBtn from './CelToFahrBtn';
class CeltoFahr extends Component {
state = {
celOn: true
};
switchCel = () => {
this.setState({ celOn: !this.state.celOn });
};
render() {
return (
<>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<CelToFahrBtn switchCel={this.switchCel} celOn={celOn} />
</div>
</div>
</div>
</div>
</>
);
}
}

Resources