Pass onClick Value From One Component to Another in React - javascript

I have two components, one which handles the buttons and another which is the index of the page.
The index of the page is structured as follows
const MainComp = () => {
const [text, setText] = useState(' ')
const test = [
'One',
'Two',
'Three',
]
return (
<>
<ChildComp onClick={() => test.map((i) => setText(i))} />
{text}
</>
)
}
In the button component I have this...
const ChildComp = ({ onClick }) => {
const test2 = [1,2,3]
return (
<>
<div>
{test2.map((data) => (
<button onClick={onClick}>
{data}
</button>
))}
</div>
</>
)
}
I want the value of each item in test to show up when the button is clicked.

Try this:
...
const test2 = [1, 2, 3]
const onClick = (item) => alert(item)
return (
<>
<div>
{test2.map((data) => (
<button onClick={() => onClick(data)}>{data}</button>
))}
</div>
</>
)

Try below code
const MainComp = () => {
const [text, setText] = useState(' ')
const test = [
'One',
'Two',
'Three',
]
return (
<>
{test.map(oneTest => <ChildComp data={oneTest} handleUpdateText={value => setText(value)}/>)}
</>
)
}
const ChildComp = ({ data, handleUpdateText }) => {
return (
<>
<button onClick={handleUpdateText}>
{data}
</button>
</>
)
}

Why don't you try to use the props the send the data from Main to the Child page..??You can do it as follows:
Main Page Code:
import React from 'react'
import Child from './Child'
const Main = ()=>{
const data = ["one","two","three"];
return(
<div>
<Child data={data}/>
</div>
)
}
export default Main;
Child Page code:
import React from 'react'
const Child =({data})=>{
const getData=()=>{
console.log(data)
}
return(
<div>
<button onClick={getData}>Transfer Data</button>
</div>
)
}
export default Child;
This way, when you click on the button the data from the Main page is transfered to the Child Page. Right now the data is showing in the console..but hey, you got the data from another page and now you can do whatever you want to do with that..!!

Related

How do I store a function call within Context

What I would like to be able to do is to initialize my context with a state and a function that updates that state.
For example, say I have the following:
export default function MyComponent () {
const MyContext = React.createContext()
const [myState, setMyState] = useState('1')
const contextValue = {
currentValue: myState,
setCurrentValue: (newValue) => setMyState(newValue)
}
return (
<MyContext.Provider value={contextValue}>
<MyContext.Consumer>
{e => <div onClick={() => e.setCurrentValue('2')}> Click me to change the value </div>}
{e.currentValue}
</MyContext.Consumer>
</MyContext.Provider>
)
}
The {e.currentValue} correctly outputs '1' at first, but when I click the button, nothing changes.
What I would expect is that e.setCurrentValue('2') would call setMyState('2'), which would update the state hook. This would then change the value of myState, changing the value of currentValue, and making '2' display.
What am I doing wrong?
You would want to return a fragment from the context as one JSX root.
Check here - https://playcode.io/931263/
import React, { createContext, useState } from "react";
export function App(props) {
const MyContext = React.createContext();
const [myState, setMyState] = useState("1");
const contextValue = {
currentValue: myState,
setCurrentValue: newValue => setMyState(newValue)
};
return (
<MyContext.Provider value={contextValue}>
<MyContext.Consumer>
{e => (
<>
<div onClick={() => e.setCurrentValue("2")}>
Click me to change the value
</div>
{e.currentValue}
</>
)}
</MyContext.Consumer>
</MyContext.Provider>
);
}
You're using e.currentValue outside of MyContext.Consumer context which does not have e, so it's throwing an error that e is not defined from e.currentValue.
You can wrap them up together under <MyContext.Consumer>{e => {}}</MyContext.Consumer>
function MyComponent() {
const MyContext = React.createContext();
const [myState, setMyState] = React.useState("1");
const contextValue = {
currentValue: myState,
setCurrentValue: (newValue) => setMyState(newValue),
};
return (
<MyContext.Provider value={contextValue}>
<MyContext.Consumer>
{(e) => (
<div>
<div onClick={() => e.setCurrentValue("2")}>
Click me to change the value
</div>
<div>{e.currentValue}</div>
</div>
)}
</MyContext.Consumer>
</MyContext.Provider>
);
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

JS: Problem with with displaying a select list in modal on page

I have a problem with displaying a list for user selection. When I open the page for the first time, it works correctly. But after I reload the page, the users "disappear" or are unloaded.
Before reload
After reload.
Here is the code I have.
My-page:
const Page = observer(() => {
const { project } = useContext(Context);
const [modalVisible, setModalVisible] = useState(false);
useEffect(() => {
fetchUsers().then((data) => project.setUsers(data));
}, []);
return (
<Container>
<Button onClick={() => setModalVisible(true)}>
ChooseUser
</Button>
<ChooseUser show={modalVisible} onHide={() => setModalVisible(false)} />
</Container>
);
});
export default Page;
Modal:
const ChooseUser = observer(({ show, onHide }) => {
const { project } = useContext(Context);
return (
<Modal show={show} onHide={onHide}>
<Form>
<Form.Select>
{/* The problem with this list */}
{project.users.map((user) =><option>{user.username}</option>)}
</Form.Select>
</Form>
</Modal>
);
});
Context creating in index.js:
export const Context = createContext(null);
ReactDOM.render(
<Context.Provider value={{
project: new ProjectStore(),
}}
>
<App />
</Context.Provider>,
document.getElementById('root'),
);
ProjectStore
export default class ProjectStore {
constructor() {
this._users = [];
makeAutoObservable(this);
}
setUsers(value) {
this._users = value;
}
get users() {
return this._users;
}
}
You might try Array.from(project.users).map((user) ... instead of project.users.map((user) ... and see if that helps.

How to pass data from mapped objects to its parent component in React?

I'm building a shopping cart app, the app includes each item as a card component. I rendered these cards by mapping some dummy object data like this:
const Home = () => {
const dummyData = [
{
id: 1,
title: 'tshirt',
price: 10
},
{
id: 2,
title: 'coat',
price: 20
}
]
const RenderCards = () => {
return (
dummyData.map(
(d) => {
return (
<Card key={d.id} title={d.title} price={d.price} handleAddToCart={handleAddToCart}/>
)
}
)
)
}
const handleAddToCart = () => {
// maybe access id and price here?
}
return (
<>
<RenderCards />
</>
)
}
and the Card component being rendered:
const Card = ({id, title, price}) => {
return (
<>
<div key={id}>
<p>{title}</>
<p>{price}</>
<button onClick={handleAddToCart}>Add to cart</button>
</div>
</>
)
}
Now on click of the button in each card, I'd like to send the data (the id of the card and the price of the item) back to the parent Home component. Say 2nd card is clicked, I want to have access to id and price in Home.
EDIT:
Maybe I didn't make myself clear, I'd like to access the clicked card's price and id in handleAddToCart function.
You can either pass the handler down and have the child pass the details to it, like this:
items.map(item => <Item addToCart={addToCart} {...item} />)
const Item = ({ id, name, addToCart }) =>
<div>
{name}
<button onClick={() => addToCart(id)}>Add to Cart</button>
</div>
Or pass down a values-included callback like this:
items.map(item => <Item addToCart={() => handleAddToCart(item.id)} {...item} />)
const Item = ({ id, name, addToCart }) =>
<div>
{name}
<button onClick={addToCart}>Add to Cart</button>
</div>
In <Home /> component, first you can introduce a new state with useState as:
const [selectedItem, setSelectedItem] = useState(null)
Then pass down through props the setSelectedItem in order to be able to trigger there:
<Card key={d.id} title={d.title} price={d.price} handleAddToCart={handleAddToCart}
setSelectedItem={setSelectedItem} />
Then in <Card /> component use as:
const Card = ({id, title, price, setSelectedItem}) => {
return (
<>
<div key={id}>
<p>{title}</>
<p>{price}</>
<button onClick={() => {
handleAddToCart();
setSelectedItem({ id, title, price});
}}>Add to cart</button>
</div>
</>
)
}
+1 suggestion:
I would pass down to <Card /> component the details in one attribute as:
<Card key={d.id}
data={d}
handleAddToCart={handleAddToCart}
setSelectedItem={setSelectedItem} />
Then destructure inside as:
const Card = (props) => {
const { data, setSelectedItem, handleAddToCart } = props
const { id, title, price } = data
// more code
}

How to pass data to {props.children}

On my follow up question from here : How to pass data from child to parent component using react hooks
I have another issue.
Below is the component structure
export const Parent: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const createContent = (): JSX.Element => {
return (
<Authorization>
{<ErrorPanel message={errorMessage} setDisabled={setDisabled}/>}
<MyChildComponent/>
</<Authorization>
);
}
return (
<Button onClick={onSubmit} disabled={disabled}>My Button</Button>
{createContent()}
);
};
const Authorization: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const render = (errorMessage : JSX.Element): JSX.Element => {
return (
<>
{<ErrorPanel message={errorMessage} setDisabled={setDisabled}/>}
</>
);
};
return (
<>
<PageLoader
queryResult={apiQuery}
renderPage={render}
/>
{props.children}
</>
);
};
How do I pass the disabled state value from Authorization component to my child which is invoked by
{props.children}
I tried React.cloneElement & React.createContext but I'm not able to get the value disabled to the MyChildComponent. I could see the value for disabled as true once the errorMessage is set through the ErrorPanel in the Authorization component.
Do I need to have React.useEffect in the Authorization Component?
What am I missing here?
You need to use React.Children API with React.cloneElement:
const Authorization = ({ children }) => {
const [disabled, setDisabled] = React.useState(false);
const render = (errorMessage) => {
return (
<>{<ErrorPanel message={errorMessage} setDisabled={setDisabled} />}</>
);
};
return (
<>
<PageLoader queryResult={apiQuery} renderPage={render} />
{React.Children.map(children, (child) =>
React.cloneElement(child, { disabled })
)}
</>
);
};
// |
// v
// It will inject `disabled` prop to every component's child:
<>
<ErrorPanel
disabled={disabled}
message={errorMessage}
setDisabled={setDisabled}
/>
<MyChildComponent disabled={disabled} />
</>
You can make use of React.cloneElement to React.Children.map to pass on the disabled prop to the immediate children components
const Authorization: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const render = (errorMessage : JSX.Element): JSX.Element => {
return (
<>
{<ErrorPanel message={errorMessage} setDisabled={setDisabled}/>}
</>
);
};
return (
<>
<PageLoader
queryResult={apiQuery}
renderPage={render}
/>
{React.Children.map(props.children, child => {
return React.cloneElement(child, { disabled })
})}
</>
);
};
UPDATE:
Since you wish to update the parent state to, you should store the state and parent and update it there itself, instead of storing the state in child component too.
export const Parent: React.FC<Props> = (props) => {
const [disabled, setDisabled] = React.useState(false);
const createContent = (): JSX.Element => {
return (
<Authorization setDisabled={setDisabled}>
{<ErrorPanel message={errorMessage} disabled={disabled} setDisabled={setDisabled}/>}
<MyChildComponent disabled={disabled}/>
</<Authorization>
);
}
return (
<Button onClick={onSubmit} disabled={disabled}>My Button</Button>
{createContent()}
);
};
const Authorization: React.FC<Props> = (props) => {
const render = (errorMessage : JSX.Element): JSX.Element => {
return (
<>
{<ErrorPanel message={errorMessage} disabled={props.disabled} setDisabled={props.setDisabled}/>}
</>
);
};
return (
<>
<PageLoader
queryResult={apiQuery}
renderPage={render}
/>
{props.children}
</>
);
};

How to pass prop to component's children

const ListView = () => {
return(
<ul>
<ListItem modal={<Modal />} />
</ul>
)
};
const ListItem = (props) => {
const [visible, setVisible] = useState(false);
const toggle = () => setVisible(!visible)
return (
<>
<li>
ListItem
</li>
<ModalWrapper toggle={toggle}>{props.modal}</ModalWrapper>
</>
)
}
const ModalWrapper = (props) => {
if(!props.visible) return null;
return (
<>
{props.children}
</>
)
}
const Modal = ({ toggle }) => {
/* I would like to use toggle() here. */
return (
<>
<div onClick={toggle} className="dimmer"></div>
<div className="modal">modal</div>
</>
)
}
I have a function toggle() in <ListItem /> as shown above.
I am struggling to use toggle() in <Modal />.
Is it possible or are there any suggestions?
You need to inject toggle to ModalWrapper children, be careful not to override toggle prop on Modal after it.
const ModalWrapper = ({ children, visible, toggle }) => {
const injected = React.Children.map(children, child =>
React.cloneElement(child, { toggle })
);
return <>{visible && injected}</>;
};
Refer to React.cloneElement and React.Children.map.
Demo:

Categories

Resources