I have a function that I declared on a file, like this
export default function doSomething (param1, param2, param3) {
*Do something here*
}
And then I want to use that function whenever I press a button on a screen I have declared in another file:
import doSomething from '../../doSomething';
export default function Screen(props) {
return (
<Container>
<SafeAreaProvider>
<Button onPress={() => doSomething(1, 2, 3)} />
</SafeAreaProvider>
</Container>
);
}
However, any time I press the button, it gives me a Invalid hook call. Hooks can only be called inside of the body of a function component error. I'm fairly new to custom hooks/external functions, so how can I resolve is?
If doSomething is a hook and not a plain JS function, then you can not just import it and call it as you would do with plain functions.
The following code fixes the issue.
export default function useDoSomething() {
...
const doSomething = React.useCallback((parameter1, parameter2, parameter3) => {}, [])
return {
doSomething,
}
}
Then use the hook as follows.
const { doSomething } = useDoSomething()
return (
<Container>
<SafeAreaProvider>
<Button onPress={() => doSomething(1, 2, 3)} />
</SafeAreaProvider>
</Container>
);
Related
I'm new to Typescript and I have to write a parent component that sends as props onClick to the child component where is a button that triggers the functionality of onClick when clicked.
Here is the code:
export function ParentComponent(props: ParentComponentProps) {
const [isOpened, toggleModal] = useToggle(false);
// useToggle is a custom hook that toggles a boolean
...
const onClick = () => {
toggleModal();
}
return (
<ChildComponent onClick={onClick} />
);
}
For the child component I don't know how to define the interface, what should be put here?
export interface ChildComponentProps {
onClick: <unknown>(); // what to add here?
}
And here is the child component:
export function ChildComponent({onClick}: ChildComponentProps) {
...
return (
<div>
...
<ButtonComponent
onClick={() => onClick()}
...
/>
...
);
}
Any ideas what to add to the interface or if there should be any other changes to be Typescript correct?
For functions like onClick, you have to describe input arugments and output or just write Function (although for better type checking you shouldn't just write Function).
So something like this:
onClick: () => void
If for example your function gets a number and returns a string, you should write it like this:
onClick: (n: number) => string
Parent Component:
interface OnClickIdInterface {
(): void,
}
export interface parentPropsInterface {
onClick?: OnGetIdInterface;
}
render() {
return <ChildComponent onClick={(id) => this.onClick(id)}/>
}
Child Component:
const HomePresentation: React.FunctionComponent<parentPropsInterface> = ({onClick}) => {
return (
<div onClick={()=>onClick()}>
some content
</div>
)
}
Following code should work. But it depends on ButtonComponent. Used React.MouseEventHandler<T = Element> as type.
export interface ChildComponentProps {
onClick: React.MouseEventHandler<ButtonComponent>
}
export function ChildComponent({onClick}: ChildComponentProps) {
...
return (
<div>
...
<ButtonComponent
onClick={() => onClick()}
...
/>
...
);
}
Please notify me if this is incorrect.
I have a component that renders:
<View>
<SomeContext.Provider value={state}>
{props.children}
</SomeContext.Provider>
</View>
I don't understand how to create a method accessible in the following way:
<View>
<SomeContext.Provider>
{(method) =>
<Button
title={"Magic"}
onPress={() => {
method()
}}
></Button>
}
</SomeContext.Provider>
</View>
Consumer Component
You need to consume a context through a Context.Consumer that is a direct descendent of the the relevant Provider. The docs have decent examples, though they mix class-based and functional components. Updating Context from a Nested Component
Sandbox: https://codesandbox.io/s/react-context-consumer-passing-method-to-nested-child-forked-u0bi8
const initData = {
data: { a: "Text from Context", b: "2" },
method: () => {
console.log("Method called.");
}
};
const SomeContext = React.createContext();
export default function App() {
return (
<SomeContext.Provider value={initData}>
<Content />
</SomeContext.Provider>
);
}
function Content() {
return (
<div>
<SomeButton />
</div>
);
}
function SomeButton() {
return (
<SomeContext.Consumer>
{({ data, method }) => <button onClick={method}>{data.a}</button>}
</SomeContext.Consumer>
);
}
useContext Hook
The useContext hook is also available, and possibly provides a more familiar pattern. The Consumer of the context still needs to be a descendant of the Provider, but it is accessed via an hook rather than through a Consumer component.
Sandbox: https://codesandbox.io/s/react-context-passing-method-to-nested-child-1fcno
const initData = {
data: { a: "Text from Context", b: "2" },
method: () => {
console.log("Method called.");
}
};
const SomeContext = React.createContext();
export default function App() {
return (
<SomeContext.Provider value={initData}>
<Toolbar />
</SomeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<SomeButton />
</div>
);
}
function SomeButton() {
const { data, method } = React.useContext(SomeContext);
return <button onClick={method}>{data.a}</button>;
}
The Context counsumer docs actually tells you everything you need to know:
A React component that subscribes to context changes. This lets you subscribe to a context within a function component.
Requires a function as a child. The function receives the current context value and returns a React node. The value argument passed to the function will be equal to the value prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the value argument will be equal to the defaultValue that was passed to createContext().
So, in your example you need to pass the method you want to the provider:
const method = () => {};
<View>
<SomeContext.Provider value={{ method }}>
{props.children}
</SomeContext.Provider>
</View>
And then in the Consumer you can call it like this:
<View>
<SomeContext.Consumer>
// using destructuring here,
// so its ({ method }) instead of (method)
{({ method }) =>
<Button
title={"Magic"}
onPress={() => method()} />
}
</SomeContext.Consumer>
</View>
Also, it's important that the consumer component is inside the provider.
I have this ParentComponent which I want to pass a function named toggleDrawer to the ChildComponent like this:
const ParentComponent = () {
const [drawerState, setDrawerState] = useState(false);
const toggleDrawer = (anchor, open) => {
setDrawerState(open);
}
return(
<div>
<IconButton color="primary"
onClick={toggleDrawer("right", true)}> // here I called to toggleDrawer so the ChildComponent can be shown
<SomeIcon />
</IconButton>
<ChildComponent
anchor="right"
open={drawerState}
handleDrawerState={toggleDrawer}/>
</div>
)
}
So I get the toggleDrawer function in ChildComponent like this:
const CartDrawer = (props) => {
// other stuff at the top
return(
<Drawer
anchor={props.anchor}
open={props.open}
onClose={props.handleDrawerState(props.anchor, false)}
>
)
}
As you can see I get the handleDrawerState in ChildComponent by accessing it props. But what I get is:
TypeError: props.handleDrawerState is not a function
I tried below, also get the same result:
const {handleDrawerState} = props
<Drawer
... other stuff
onClose={handleDrawerState(props.anchor, false)}
>
So I check the console in browser by console.log(props) , instead having a key with handleDrawerState, I having a object in the props ,which present like this:
proto: Object
For now, I not understand what I doing wrong, cause as I see here, toggleDrawer in ParentComponent is a function, but passed to ChildComponent it become and object. Therefore I unable to access it in props in ChildComponent .
Question:
Therefore, what is the correct way to pass a function to ChildComponent ?
Updated:
If I do like this :
<Drawer
... some other stuff
onClose={() => props.handleDrawerState(props.anchor, false)}
>
I get the error like this:
Uncaught Error: Too many re-renders. React limits the number of
renders to prevent an infinite loop.
They need to be wrapped in an anon function
You cannot call a function as a prop if you fire the function when adding it as a prop (unless you want the result of the fired function passed as a prop).
Should be this
const ParentComponent = () {
const [drawerState, setDrawerState] = useState(false);
const toggleDrawer = (anchor, open) => {
setDrawerState(open);
}
return(
<div>
<IconButton color="primary"
onClick={() => toggleDrawer("right", true)}> //anon func here
<SomeIcon />
</IconButton>
<CartDrawer
anchor="right"
open={drawerState}
handleDrawerState={toggleDrawer}/>
</div>
)
}
const CartDrawer = (props) => {
// other stuff at the top
return(
<Drawer
anchor={props.anchor}
open={props.open}
onClose={() => props.handleDrawerState(props.anchor, false)} // annon func here
/>
)
}
The way you have it right now it will fire only once when the component mounts.
<MyComponent
onClick={handleClick()} // this will always fire on mount, never do this unless you want the result from the function as a prop and not the function as itself
/>
I want to pass two functions to onClick event which is handleSubmit and handleDelete to the HomePage.js from the HomeItem.js
Here is my Error:
No duplicate props allowed react/jsx-no-duplicate-props.
Here is my HomePage.js:
const HomePage = props => {
const tvshow = props.item;
let res;
if (tvshow.length > 0) {
res = tvshow.map(res=> (
<Content item={res} onClick={props.onClick}/>
));
}
return (
<div>
<Container>
<Row>{res}</Row>
</Container>
</div>
);
};
export default HomePage;
Here is my HomeItem.js:
const HomeItem = props => {
function handleSubmit() {
props.onClick({
name: props.item.name,
id: props.item.id
});
}
function handleName() {
props.onClick({
name: props.item.name
});
}
<Button onClick={handleSubmit}></Button>
<Button onClick={handleName}></Button>
Here is my App.js:
handleSubmit(newFavorite) {}
handleName(newFavorite) {}
render() {
<Route
exact
path="/"
render={() => (
<HomePage
item={this.state.SaveFavorite}
onClick={this.handleSubmit}
onClick={this.handleName}
/>
)}
/>
}
So my question is how to put 2 onClick function to the Hompage.js
How about this:
<HomePage
item={this.state.SaveFavorite}
onClick={(favorite)=>{
this.handleSubmit(favorite);
this.handleName(favorite);
}
}
/>
This assumes your goal is to call both functions one at a time. If they should be called in different situations give one function a different name, eg onSubmit or onNameChange.
Try This:
<HomePage
item={this.state.SaveFavorite}
onClick={(newFavorite) => this.handleSubmit(newFavorite);this.handleName(newFavorite)}
/>
you can pass multiple functions to events in react, let say changeEvent, to do follow those steps.
1- create your function two or the number of function you like.
2- create an object that contains those functions
3- pass the object as a props to where it would be consumed
4- choose the correspondant function to each form or whatever you need.
here is an example, this sample is with typescript.
const onChangeFunctions = {
onChangeForm1: handleChangeForm1,
onChangeForm2: handleChangeForm2,
};
<MainForm
onChange={onChangeFunctions} // here is your function
datas={yourData}
otherProps={otherProps}
/>
Now you use the fucntion on the child components
interface PropsFrom {
model1: Model1;
model2: Model2;
onChange: any;
}
export default function ProductForm(props: PropsForm) {
return (
<Container maxWidth="lg">
<Grid item md={6}>
<FormOne
model={props.model1} // the model that bind your form
onChange={props.onChange.onChangeForm1} // here you can use your first function
/>
</Grid>
<Grid item md={6}>
<FormTwo
model={props.model2} // the model that bind your form
onChange={props.onChange.onChangeForm2} // here you can use your second function
/>
</Grid>
</Grid>
</Container>
for javascript just pass the functions as props and delete the interface from the child components.
My react component is like this i am rendering part of component using getMenuItems method.
const TransactionListFilter = (props) => {
const getMenuItems = () => filterCriteriaItems.map((data) =>
<MenuItem value={data.valu} name={data.name} >{data.name}</MenuItem>,
);
return (
<Dropdown
className="Transaction-List-Filter-container-dropdown"
selectMode="single"
defaultText="See All"
onMenuItemClick={handleFilterChange}
>
<Menu>
{
getMenuItems()
}
</Menu>
</Dropdown>
);
};
How i can test method getMenuItems any Idea?
The problem is that you are hiding the method getMenuItems to be an internal working of the TransactionListFilter function. Are you sure you want to test it? As soon as you refactor the TransactionListFilter method, your test will break. As far as I see, you have 2 options:
Refactor to a class:
class TransactionListFilter {
getMenuItems(){
return filterCriteriaItems.map((data) =>
<MenuItem value={data.valu} name={data.name}>{data.name}</MenuItem>,
);
}
render() {
return (
<Dropdown
className="Transaction-List-Filter-container-dropdown"
selectMode="single"
defaultText="See All"
onMenuItemClick={handleFilterChange}
>
<Menu>
{
this.getMenuItems()
}
</Menu>
</Dropdown>
);
}
};
Hacky translation of the function (e.g. converting the function to a string, then parsing the string up until the return, and passing the result to the Function constructor). That would look something like this: (at your own risk)
const getMenuItems = new Function('', `${TransactionListFilter.toString().match(/\getMenuItems.*=.*=>(.*)\)/s)[1]};`)