I have a button that is going to have a onClick that is hardcoded and another one that however uses the component, can create his custom onClick. I don't believe this is a duplicated since all the questions i was able to found advise to use both onClick's in a hardcoded way, that can't be changed when using the Button component.
This the component:
const Button = props => {
const { children, toggle, ...other } = props;
const [pressed, setPressed] = useState(true);
const renderPressedButton = e => {
setPressed(!pressed);
onClick(); //here onClick should be called
};
return (
<StyledButton
toggle={toggle}
pressed={toggle ? pressed : null}
onClick={toggle && renderPressedButton}
{...other}>
{children}
</StyledButton>
);
};
I have also tried this function on the onClick event:
const onClickHandler = () => {
renderPressedButton();
onClick();
}
And using the component i would have my custom onClick
const anotherClickHandler = () => {
console.log('Hey, this is a function outside Button component');
}
<Button onClick={anotherClickHandler}>Button</Button>
What i have tried is calling the onClick(); inside the renderPressedButton from the first onClick but it did not work. The second onClick(anotherClickHandler) is called but not the first one(renderPressedButton).
What i'm doing wrong?
I have found the solution.
On the main Button:
const Button = props => {
const { onClick } = props; //first i add onClick as a prop
const [pressed, setPressed] = useState(true);
const renderPressedButton = () => {
setPressed(!pressed);
if (onClick) {//then i check, if theres an onClick, execute it
onClick();
}
};
return (
<StyledButton
pressed={pressed}
{...props}
onClick={renderPressedButton}
/>
);
};
And when using the Button:
<Button onClick={justASimpleOnClickHandler}>Toggle</Button>
This way i can have my main onClick function and another one when using my component.
Related
hi is it safe to call function props inside setState function param?
(if onToggle is used by the parent for setting another state), how it's affect the lifecycle / event persist?
example:
const myComponent = ({onToggle}) => {
const [active, setActive] = useState(false);
//use this
const handleChange = (e) => {
setActive(prev => {
onToggle(e, !prev);
return !prev;
});
}
//instead of this
const handleChange = (e) => {
setActive(prev => !prev);
onToggle(e. !active);
}
return (
<>
<button type="button" onChange={handleChange} />
{active && <div>ACTIVE</div>}
</>
)
}
because react says that update state may asynchronous:
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
In App.Js file I'm passing "This data is coming from Parent" to Child.Js
But here I want to pass this on button through onClick not dataParentToChild={data}. Just Like Child.Js file working in onClick={handleChildToProp}
enter the link description here
App.Js
function App(props) {
// Child To Parent
const [word, setWord] = useState("");
const handleChildToParent = (words) => setWord(words);
// Parent To Child
const data = "This data is coming from Parent";
return (
<>
<h1>"Parent Data"</h1>
<h2>{word}</h2>
<Child
// Without button Working Fine
dataParentToChild={data}
// With button Working Fine
dataChildToParent={handleChildToParent}
/>
</>
);
}
Child.Js
const Child = (props) => {
// Parent To Child
const handleChildToProp = () => {
props.dataChildToParent("This data is comming from Child");
};
return (
<>
<h1>Child Components</h1>
<h2>{props.dataParentToChild}</h2>
<button onClick={handleChildToProp}>Data Child To Parent</button>
</>
);
};
You need to make the button onClick update a state to trigger a re-render of dataParenToChild, like so:
function App(props) {
// Child To Parent
const [word, setWord] = useState('');
const [parentToChild, setParentToChild] = useState('');
const handleChildToParent = (words) => setWord(words);
// Parent To Child
const handleParentToChild = () => {
setParentToChild('This data is coming from Parent');
};
return (
<>
<h1>"Parent Data"</h1>
<h2>{word}</h2>
<button onClick={handleParentToChild}>Data Parent To Child</button>
<Child
// Without button Working Fine
dataParentToChild={parentToChild}
// With button Working Fine
dataChildToParent={handleChildToParent}
/>
</>
);
}
Working solution
I've got two splitted functions and I need to pass a value from one to the other, which I'm doing like shown below. What is the difference between const handleClick = icon.onClick(category) and const handleClick = () => icon.onClick(category)?
And how do I pass the event from the component to the handleClick() function?
export const useCategories = () => {
const handleClick = (category, something) => {
event.stopPropagation() // <-- 3. How to get event?
console.log(category, something) // <-- 4. Get every value
}
return {
icon: {
onClick: (category) => handleClick(category, 'anything') // <-- 2. add second var value
}
}
}
export const Categories = () => {
const { icon } = useCategories()
return (
<div>
{categories.map((category) => {
const handleClick = icon.onClick(category) // <-- 1. pass category value
return <Icon onClick={handleClick} />)}
}
</div>
)
}
You'll need to proxy the event object through on all click handlers. I like to use curried functions to make attaching the click handler a little simpler. Don't forget to add a react key to the mapped icons.
export const useCategories = () => {
const handleClick = (event, category, something) => {
event.stopPropagation();
console.log(category, something);
};
return {
icon: {
// curried function to receive category and return onClick handler
onClick: category => event => handleClick(event, category, 'anything'),
}
}
}
export const Categories = () => {
const { icon } = useCategories();
return (
<div>
{categories.map((category, index) => (
<Icon
key={index}
onClick={icon.onClick(category)} // <-- set category
/>
)
</div>
);
}
The onevent handlers are properties on certain DOM elements to manage how that element reacts to events.
When the event handler is specified as an HTML attribute, the specified code is wrapped into a function with the following parameters:
event — for all event handlers except onerror.
event, source, lineno,colno, and error for the onerror event handler.
Note that the event parameter actually contains the error
message as a string.
When the event handler is invoked, the this keyword inside the handler is set to the DOM element on which the handler is registered. For more details, see, see the this keyword documentation.
if you want more see, this
but in your code, you need to add this
<Icon onClick={(event) => icon.onClick(event, category, ...rest)} />
As you want to pass the event and category to the click handler, modify the function passed to onClick to pass the args.
export const useCategories = () => {
const handleClick = (event, ...rest) => {
event.stopPropagation();
console.log(rest);
};
return {
icon: {
onClick: (event, category) => handleClick(event, category, 'anything'),
},
};
};
export const Categories = () => {
const { icon } = useCategories();
return (
<div>
{categories.map((category) => {
return <Icon onClick={(event) => icon.onClick(event, category)} />;
})}
</div>
);
};
When you dont need to pass parametrs you use
onClick={func}
When you want to pass parametrs you use
onClick={() => func(someParmeter)}
To pass the event simply write
onClick={e => func(e)}
I'm working on a modal function in an application. Since the app has different modals, I have a function which handles the open & close state of various windows:
OpenItem.jsx
const OpenItem = ({ toggle, content }) => {
const [isShown, setIsShown] = useState(false);
const hide = () => setIsShown(false);
const show = () => setIsShown(true);
return (
<>
{toggle(show)}
{isShown && content(hide)}
</>
);
};
export default OpenItem;
Header.jsx
Now in my main component, I want to to use this function with another component:
const Header = () => {
return (
<div>
<OpenItem
toggle={(show) => <Button onClick={show}>icon</Button>}
content={(hide) => (
// Component to hide:
<ComponentToShowOrHide onClick={hide} />
)}
/>
</div>
);
};
export default Header;
This works fine, except that instead of having the {hide} function as a part of the imported component, I want to toggle the view in <Button onClick={show}>icon</Button>
My idea is to conditionally render the show or hide in the button instead of rendering it in the component, but I'm not quite sure how to do that since I haven't used an outside function to control a function in a component.
Simply write a function that toggles the state rather than sets it to a value.
const OpenItem = ({ toggle, content }) => {
const [isShown, setIsShown] = useState(false);
return (
<>
{toggle(() => setIsShown(prevState => !prevState))}
</>
);
};
export default OpenItem;
Lets say I have a components array in my React app:
const deleteProject = useCallback(project => {
// something
}, []);
return (
projects.map(p => (
<button onClick={() => deleteProject(p)}>Delete</button>
);
);
Is there any way I could use just deleteProject function without wrapping it into separate callbacks i.e. {} => {} for each component? This is for performance purposes. I mean something like:
<button onClick={deleteProject}>Delete</button>
And then in deleteProject somehow I'd need to determine which project to delete, but how? It only takes click event as argument
If you have a long projects list and see performance issues you could define DeleteButton component to avoid button re-rendering
const DeleteButton = ({project, deleteProject}) => {
const onClick = useCallback(
() => deleteProject(project),
[project, deleteProject],
);
return <button onClick={onClick}>Delete</button>
}
const YourComponent = ({projects, deleteProject}) => (
<>
{projects.map(project => <DeleteButton {...{project, deleteProject}}/>)
</>
)
You can achieve by assigning project identifier to button as below
const deleteProject = event => {
projectId = event.target.id;
// Delete project here using id
}
return (
projects.map(p => (
<button id={p.id} onClick={deleteProject}>Delete</button>
);