How to pass a state (hook) between separated files (components) in React - reactjs

Btw I already did a lot of research trying to solve this; So far I'm still stuck on this:
This is the first component in file1.jsx:
import * as React from 'react';
export default function MenuPopupState(props){
const [career, setCareer] = React.useState('');
return (
<div>
<button onClick={() => { setCareer(career) }}>
Press me
</button>
</div>
)
}
This is the another component in file2.jsx where I want to use the career state:
import * as React from 'react';
import MenuPopupState from './components/Menupopup';
export default function Header(){
const career = MenuPopupState.career;
return (
<div>
<a key={career.id}>
{career.name}
</a>
</div>
)
}
I tried differents methods, without reaching my goal of use the career.name in the Header

If state is shared between components, then you need to elevate that state to a common parent.
Then you can pass that state down to the children, probably as props or context.
For example:
function App() {
const [career, setCareer] = React.useState('');
return <>
<MenuPopupState career={career} onClick={setCareer} />
<Header career={career} />
</>
}
function MenuPopupState(props){
return (
<div>
<button onClick={() => props.onClick(props.career)}>
Press me
</button>
</div>
)
}
function Header(props){
return (
<div>
<a key={props.career.id}>
{props.career.name}
</a>
</div>
)
}

Related

Add element in array by button click

I have product cards that are rendered based on a json file.
By clicking on the "Add to Cart" button, the element should be added to the array сartList, but this does not happen.
I also tried to forward the function to the component itself, but it didn’t work out too well for me.
Shop.jsx:
import React, { useState } from 'react';
import './Instruments.css';
import Cart from '../components/Cart'
import Product from '../components/Product'
import cart from '../img/cart.png';
import data from "../data/data.json";
unction Shop() {
const [value, setValue] = useState('');
const [currentData, setCurrentData] = useState(data);
const [cartList, setCartList] = useState([]);
return (
<div className='shop'>
<div className='container'>
<div className='shop__main-products'>
{
currentData.filter((el) => {
return value.toLowerCase() === '' ? el : el.title.toLowerCase().includes(value.toLowerCase())
}).map((el, index) => {
return (
<Product img={el.img} title={el.title} price={el.price} key={el.id} onClick={() => setCartList([...cartList, el])}/>
)
})
}
</div>
</div>
<Cart active={modalActive} setActive={modalSetActive}/>
</div>
</div>
);
}
export default Shop;
Product.jsx:
import React, { useState } from 'react';
import './Product.css';
function Product({img, title, price, id, type}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button'>В корзину</button>
</div>
</div>
)
}
export default Product;
It looks like the issue is with how you're passing the onClick function to the Product component. The onClick prop should be passed to the "Add to Cart" button, not the Product component itself. You should change the following line:
<Product img={el.img} title={el.title} price={el.price} key={el.id} addToCart={() => setCartList([...cartList, el])}/>
And in the Product component, you should add the onClick prop to the "Add to Cart" button:
<button className='product__buy-button' onClick={addToCart}>В корзину</button>
This way, when the button is clicked, it will call the addToCart function and add the element to the cartList array.
You are not adding the onClick function to the props of the Product component pass it down the pipe and itll work.
function Product({img, title, price, id, type, onClick}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button' onClick={onClick}>В корзину</button>
</div>
</div>
)
}

Trigger events upwards for custom components

I have a custom made component called CoolButton.
If I involve the component in another component like below, I'd somehow like to trigger the onClick-event, just like if it was a normal button.
A second question (not as important), is if it's possible to access the Press Me-value which was passed down the the CoolButton-component, in the child/CoolButton-Component. I'd much rather want Press Me to be dislayed instead of I'm button
export default function Home() {
[timesClicked, setTimesClicked] = useState(0)
return (
<div className="home">
<h1>Click the button!</h1>
<div>The CoolButton has been clicked {timesClicked} times!</div>
<CoolButton onClick={() => setTimesClicked(timesClicked + 1)}>Press Me</CoolButton>
</div>
);
}
export default function CoolButton() {
return (
<div className="cool_button">
<button>I'm button</button>
</div>
);
}
You are passing onClick as a prop to CoolButton component. Unless you use that prop in your component, it would not make any difference. Regarding your second question, if you would like wrap your component around some content, you should provide children in the component. Something like this:
export default function Home() {
[timesClicked, setTimesClicked] = useState(0)
return (
<div className="home">
<h1>Click the button!</h1>
<div>The CoolButton has been clicked {timesClicked} times!</div>
<CoolButton onClick={() => setTimesClicked(timesClicked + 1)}>Press Me</CoolButton>
</div>
);
}
export default function CoolButton(props) {
return (
<div className="cool_button">
<button onClick={props.onClick}>{props.children}</button>
</div>
);
}
You should pass the state variables down to the button and call them on the onClick event of the <button>.
You can pass a custom title to the button as prop as well
export default function Home() {
const [timesClicked, setTimesClicked] = useState(0);
return (
<div className='home'>
<h1>Click the button!</h1>
<div>The CoolButton has been clicked {timesClicked} times!</div>
<CoolButton
setTimesClicked={setTimesClicked}
title='Press Me'
/>
</div>
);
}
export default function CoolButton({ title, setTimesClicked }) {
return (
<div className='cool_button'>
<button onClick={() => setTimesClicked((oldTimes) => oldTimes + 1)}>{title}</button>
</div>
);
}

Updating useState from components

I am learning ReactJS (slowly it seems) and was wondering how the below would work:
I have a search function that the user can enter a word(s) into and it spits out a load of variations of the word entered. The word is put into a button that I would like the user to be able to click and have the whole thing happen again (rather than them having to enter the name in manually).
I can't figure out how to update the useState from the NameList.js file. (I currently get the Error: React Hook "useState" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function) Thanks in advance!
NameList.js
export default function NamesList({ namesList }) {
var names_generated = arrayMaker(generator(split_name(namesList.toString())))
return (
names_generated.map(names_generated => {
return(
<div className="col-4 col-xl-2 col-xxl-1">
<button className="sug-names">{names_generated.name}</button>
</div>
)
})
)
}
Home.js
export default function Home() {
const [namesList, setNamesList] = useState([])
const inputName = useRef()
function getInputName(e){
e.preventDefault();
const name = inputName.current.value
if (name === '') return
setNamesList([name])
}
return (
<>
<div className="d-flex align-items-center justify-content-center">
<div className="">
<Container>
<Form>
<div>
<Form.Control ref={inputName} id="input_name" name="username" type="text" placeholder="Desired Name" />
<Button onClick={getInputName} variant="primary" type="submit"></Button>
</div>
</Form>
</Container>
<Container>
<div className='row'>
<NameList namesList={namesList} />
</div>
</Container>
</div>
</div>
<div>0 Names Found</div>
</>
)
}
I guess it happened because of two things,
The first is that the child should not be in the form of:
“function NamesList(){}”
but rather must be in the form of:
“const NamesList =() =>{}”
and as for the parent, it must be in the form of:
“function home(){}”
and not
“const NamesList = () =>{}”
And the second thing is to try to delete the parentheses like this code:
const NamesList = (namesList) => {
var names_generated = arrayMaker(generator(split_name(namesList.toString())))
return (
names_generated.map(names_generated => {
return(
<div className="col-4 col-xl-2 col-xxl-1">
<button className="sug-names">{names_generated.name}</button>
</div>
)
})
)
}
export default NamesList ;
also edit this function in Home file:
function getInputName(e){
e.preventDefault();
const name = inputName.current.value
if (name === '') return
setNamesList(name)
}

i`m a React beginner and function setState() is not working, and i need your help guys

I'm currently studying React and tried to make To-do list. Below is the source code.
I want to change the state "display" of the Frame component by clicking the button. I found out that the handler I addEventListener'd to the button element worked, but the state display never changed and even there are no errors on console. Why?
Frame Component
class Frame extends Component {
state = {
list: ["왜", "안돼냐"],
display: "before!!!",
};
registerHandler = () => {
this.setState = {
display: "after",
};
};
render() {
return (
<div className="frame">
<h1>To-Do-Lists</h1>
<h2>{this.state.display}</h2>
<div className="wrapper">
<Lists list={this.state.list} />
</div>
<div className="action">
<Button name="Register" onClick={this.registerHandler} />
</div>
</div>
);
}
}
export default Frame;
Button 관련 부분
import React, { Fragment } from "react";
const button = (props) => {
return (
<Fragment>
<button onClick={props.onClick} className="action button">
{props.name}
</button>
</Fragment>
);
};
export default button;
Mby you trying to set this.setState equal to something, try use it like this this.setState({})

How to pass Mobx store as props to react compoent

I have this app that uses mobx, in it there is a component called "Listings" that uses some state from mobx to render a list of items.
The way it is right now, is that the Listings component gets the data it needs(store.restaurantResults[store.selectedFood]) from inside of it by using the mobx store like so:
const Listings = () => {
const store = React.useContext(StoreContext);
return useObserver(() => (
<div className="pa2">
{store.restaurantResults[store.selectedFood] &&
store.restaurantResults[store.selectedFood].map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<p>{rest.name}</p>
</div>
);
})}
</div>
));
};
But i think this is wrong, as it couples the component with the data, I want instead to pass that data via props so it can be reusable.
What is the correct way to do this? Right now my App looks like this, where it's being wrapped around a storeProvider:
function App() {
return (
<StoreProvider>
<div className="mw8 center">
<Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way"/>
<FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
<p className="b tc pt3">or...</p>
<Search />
<Listings />
</div>
</StoreProvider>
);
}
My idea is to extract everrything inside the StoreProvider into another component that has a store and returns the jsx via useObserver so that I can acces the store and then pass what i need as props to the other components. like this:
const Wapper = () => {
const store = React.useContext(StoreContext);
return useObserver(() => (
<div className="mw8 center">
<Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way" />
<FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
<p className="b tc pt3">or...</p>
<Search />
<Listings listings={store.restaurantResults[store.selectedFood]} />
</div>
))
}
And then on the listings component change the hard coded store.restaurantResults[store.selectedFood] inside to use the props that is being passes now, that is called listigs like so:
const Listings = ({listings}) => {
const store = React.useContext(StoreContext);
return useObserver(() => (
store.loading
? <Loading />
: <div className="pa2">
<div className="flex flex-wrap">
{listings &&
listings.map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<img className='object-fit' src={rest.image_url} alt="restuarant" />
<p>{rest.name}</p>
<p>{rest.location.address1}</p>
</div>
);
})}
</div>
</div>
));
};
And this works, but is this the right way to go about this?
As <Listings/> can be provided with listing and loading you can:
const Listings = ({listings, loading}) => {
if(loading) return <Loading />
return (
<div className="pa2">
<div className="flex flex-wrap">
{listings && listings.map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<img className='object-fit' src={rest.image_url} alt="restuarant" />
<p>{rest.name}</p>
<p>{rest.location.address1}</p>
</div>
);
})}
</div>
</div>
);
}
No observables used, no useObservable required.
You want to useObservables on store for listings then no reason to wrap all components with useObservable. You should wrap <Listings/> only.
I usually define my store as a global, so every component has visibility of it:
class Store {
#observable myVar
}
global.store = new Store()
And in my components i just use it:
#observer
export default class MyComponent extends React.Component {
constructor () {
super()
store.myVar = 0
}
setMyVar (a) {
store.myVar += 1
}
render () {
return <button onClick={this.setMyVar}>
Clicked {store.myVar} times
</button>
}
}

Resources