Functional Components: Accessing Shared States - javascript

How can I alter the state of the hook isSignup per click of a button and return it to Auth.js using functional components?
I can't figure out how to wrap the return statement inside the button. I keep getting _onClick is not a function error.
Note: Homepage.js and Auth.js are functional components and they are using shared states.
// Homepage.js
const Homepage = () => {
const [isSignup, setIsSignup] = useState(false);
const handleClick = (e) => {
if (e) {
return <Auth isSignup={true}></Auth>
}
return <Auth isSignup={false}></Auth>
}
return (
<div>
<Button component={Link} to="auth" onClick={() => handleClick(false)}>
login
</Button>
<Button component={Link} to="auth" onClick={() => handleClick(true)}>
signup
</Button>
</div>
}
// Auht.js
const Auth = (props) => {
if(props.isSignup) {
// display signup form....
}

onClick is expecting a function, so,
onClick={handleClick}
The signature of handleClick is like this,
const handleClick = (event : React.ChangeEvent<any>) => {}
From the event, you can get the button id or something to determine the action.

Related

React memo creates rerender

I'm having an issue with react memo when using nextjs. In the _app e.g. I have a button imported:
import { ChildComponent } from './ChildComponent';
export const Button = ({ classN }: { classN?: string }) => {
const [counter, setCounter] = useState(1);
const Parent = () => {
<button onClick={() => setCounter(counter + 1)}>Click me</button>
}
return (
<div>
{counter}
<Parent />
<ChildComponent />
</div>
);
};
Child component:
import React from 'react';
export const ChildComponent = React.memo(
() => {
React.useEffect(() => {
console.log('rerender child component');
}, []);
return <p>Prevent rerender</p>;
},
() => false
);
I made one working in React couldn't figure it out in my own app:
https://codesandbox.io/s/objective-goldwasser-83vb4?file=/src/ChildComponent.js
The second argument of React.memo() must be a function that returns true if the component don't need to be rerendered and false otherwise - or in the original definition, if the old props and the new props are equal or not.
So, in your code, the solution should be just change the second argument to:
export const ChildComponent = React.memo(
() => { ... },
// this
() => true
);
Which is gonna tell React that "the props didn't change and thus don't need to rerender this component".
So my issue was that I made a function called Button and returned inside a button or Link. So I had a mouseEnter inside the button which would update the state and handle the function outside the function. Kinda embarrassing. This fixed it. So the only change was I moved usestate and handlemousehover inside the button function.
const Button = () => {
const [hover, setHover] = useState(false);
const handleMouseHover = (e: React.MouseEvent<HTMLElement>) => {
if (e.type === 'mouseenter') {
setHover(true);
} else if (e.type === 'mouseleave') setHover(false);
};
return (
<StyledPrimaryButton
onMouseEnter={(e) => handleMouseHover(e)}
onMouseLeave={(e) => handleMouseHover(e)}
>
<StyledTypography
tag="span"
size="typo-20"
>
{title}
</StyledTypography>
<ChildComponent />
</StyledPrimaryButton>
);
};

How can I use an async function to await for an onClick() event (Choice between 2 Buttons in a Modal)

Some context: I need an async await function or Promise/Resolve to return the button of choice a user selects. The reason I need it this way is because I'm calling a deletePost function from within a specific child Post component and I need a Modal that exists at a parent component level to prompt the user to confirm a Delete or Cancel. I then need that decision to be returned to the child Post component to continue on with the deletion process or cancel it depending on what button the user clicks in the Modal.
The reason it has to be this way is because all the data for the post I want to delete exists at that child Post component level so the entire functionality for deletion of a post needs to be at the Post component level and I'm looking to link that to a Modal at a higher parent level which will allow me to confirm of cancel that deletion process at the click of a button on the modal
import React, { useState } from "react";
const Example = async () => {
const [modalOpen, setModalOpen] = useState(false);
const toggleModal = async () => {
// Opens Modal
setModalOpen(!modalOpen);
let decision = null; // should be "delete" or "cancel" for return
/*
This toggleModal() function will be called from within a child
component's deletePost() function to prompt a user for confirmation
of deletion or cancellation. Once the modal opens up, I believe I
need some kind of async await function HERE to wait on a click
decision from the user and then return that selected value all the
way back to the deletePost() function that was called from within
the child Post component.
*/
return decision;
};
const modalStyle = {
display: modalOpen ? "block" : "hidden",
};
return (
<div>
<button onClick={toggleModal}>Open Modal</button>
<Modal style={modalStyle}>
{/* How to link these buttons to the async await function above */}
<button>Delete</button>
<button>Cancel</button>
</Modal>
{posts.map((post, index) => (
<Post
key={post.id}
post={post}
toggleModal={toggleModal}
/>
))}
</div>
);
};
export default Example;
I am not sure about your exact use case, but if you want to know whether user selected Cancel or Confirm, you can do that with attaching separate onClick event on those two buttons.
import React, { useState } from "react";
const Example = async () => {
const [modalOpen, setModalOpen] = useState(false);
const toggleModal = () => {
// Opens Modal
setModalOpen(!modalOpen);
};
const handleConfirm = () => { //confirm logic }
const handleCancel = () => { //cancel logic }
const modalStyle = {
display: modalOpen ? "block" : "hidden",
};
return (
<div>
<button onClick={toggleModal}>Open Modal</button>
<Modal style={modalStyle}>
<button onClick={handleConfirm}>Confirm</button>
<button onClick={handleCancel}>Cancel</button>
</Modal>
</div>
);
};
export default Example;
This way you keep all the methods separate and don't even need to use async await, i.e. use for user to either click one of those buttons.
Edit: Added this codesandbox based on my understanding of op's comment.
I just published a package to handle your case.
Installation:
npm install use-awaitable-component
Usage:
import React, { useState } from "react";
import useAwaitableComponent from "use-awaitable-component";
const Example = async () => {
const [awaitModalStatus, awaitModalExec, awaitModalResolve, awaitModalReject, awaitModalReset] = useAwaitableComponent();
const modalOpen = awaitModalStatus === 'awaiting' // Show modal while awaiting
const awaitModal = async () => {
try {
const decission = await awaitModalExec();
console.log(decission);
} finally {
awaitModalReset()
}
};
const modalStyle = {
display: modalOpen ? "block" : "hidden",
};
return (
<div>
<button onClick={awaitModal}>Open Modal</button>
<Modal style={modalStyle}>
<button onClick={() => awaitModalResolve('delete')}>Delete</button>
<button onClick={() => awaitModalReset()}>Cancel</button>
</Modal>
{posts.map((post, index) => (
<Post
key={post.id}
post={post}
toggleModal={awaitModal}
/>
))}
</div>
);
};
export default Example;

React JS. remove function from button after button is pressed

How to unmount. Return does not work. I need to be able to press button and after function completes then the function will be remove from the button
const handleSubmit = () => {
ipcRenderer.send(
"EDITOR",
[ type, typeValue],
"editor_response"
);
onClose(selectedValue);
};
return <div>
<Button onClick={handleSubmit} color="primary">
Ok
</Button>
</div>
Probably the cleanest solution is a state variable that determines whether the handler should be active or not.
const MyComp = () => {
const [pressed, setPressed] = useState(false;
const handleSubmit = () => {
setPressed(true);
ipcRenderer.send(
"EDITOR",
[ type, typeValue],
"editor_response"
);
onClose(selectedValue);
};
const noop = () => {};
return <div>
<Button onClick={!pressed ? handleSubmit : noop} color="primary">
Ok
</Button>
</div>;
};

How to set the state using context.provider in react and typescript?

i am using context.provider usecontext reacthook to show a dialog. i set this around MainComponent. For the value attribute of context.provider i get error type {setDialogOpen(Open: boolean) => void} is not assignable to type boolean.
what i am trying to do?
I want to display a dialog when user clicks either a button in home or books component. on clicking hide button in dialog the dialog shouldnt be open.
below is my code,
function MainComponent() {
const DialogContext = React.createContext(false);
let [showDialog, setShowDialog] = React.useState(false);
return (
<DialogContext.Provider value={{
setDialogOpen: (open: boolean) => setShowDialog(open)}}>//get error
{showDialog && <Dialog DialogContext={DialogContext}/>
<Route
path="/items">
<Home DialogContext={DialogContext}/>
</Route>
<Route
path="/id/item_id">
<Books DialogContext={DialogContext}/>
</Route>
</DialogContext.Provider>
)
}
function Home({DialogContext} : Props) {
const dialogContext= React.useContext(DialogContext);
const handleClick = (dialogContext: any) {
dialogContext.setDialogOpen(true);
}
return (
<button onClick={handleClick(dialogContext)}>button1</button>
)
}
function Books({DialogContext} : Props) {
const dialogContext= React.useContext(DialogContext);
const handleClick = (dialogContext: any) {
dialogContext.setDialogOpen(true);
}
return (
<button onClick={handleClick(dialogContext)}>button2</button>
)
}
function Dialog({DialogContext}: Props) {
return(
<div>
//sometext
<button> hide</button>
</div>
)
}
I have tried something like below,
return (
<DialogContext.Provider value={{
setShowDialog(open)}}>//still get a error
{showDialog && <Dialog DialogContext={DialogContext}/>
)
Could someone help me fix this or provide a better approach to show the dialog on clicking a button in home and books component using usecontext hook. thanks.
There are few issues that you have to fix in your code.
You are creating context with the default value of false. Then later you try to override it to an object and hence the type error.
To fix the issue, create & export the context in separate file/helper. Don't pass them down as props.
import the context in parent and child components.
your handleClick fun in child component is missing an arrow.
the button onClick in child component is directly calling the function. You should pass just the function reference.
See the updated code with corrections below.
context helper
...
type ContextProps = {
setDialogOpen?: (open: boolean) => void,
};
export const DialogContext = React.createContext<ContextProps>({});
MainComponent
import {DialogContext} from '../contextHelper';
function MainComponent() {
// const DialogContext = React.createContext(false); //<---- remove this
let [showDialog, setShowDialog] = React.useState(false);
return (
<DialogContext.Provider value={{
setDialogOpen: (open: boolean) => setShowDialog(open)}}>
...
Home & Book Component
import {DialogContext} from '../contextHelper';
function Home() {
const dialogContext= React.useContext(DialogContext);
const handleClick = () => {
dialogContext.setDialogOpen(true);
}
return (
<button onClick={handleClick}>button1</button>
)
}
import {DialogContext} from '../contextHelper';
function Books() {
const dialogContext= React.useContext(DialogContext);
const handleClick = () => {
dialogContext.setDialogOpen(true);
}
return (
<button onClick={handleClick}>button2</button>
)
}

set state is not updating state

I am trying to use the state hook in my react app.
But setTodos below seems not updating the todos
link to my work: https://kutt.it/oE2jPJ
link to github: https://github.com/who-know-cg/Todo-react
import React, { useState } from "react";
import Main from "./component/Main";
const Application = () => {
const [todos, setTodos] = useState([]);
// add todo to state(todos)
const addTodos = message => {
const newTodos = todos.concat(message);
setTodos(newTodos);
};
return (
<>
<Main
addTodos={message => addTodos(message)}
/>
</>
);
};
export default Application;
And in my main.js
const Main = props => {
const input = createRef();
return (
<>
<input type="text" ref={input} />
<button
onClick={() => {
props.addTodo(input.current.value);
input.current.value = "";
}}
>
Add message to state
</button>
</>
);
};
I expect that every time I press the button, The setTodos() and getTodos() will be executed, and the message will be added to the todos array.
But it turns out the state is not changed. (still, stay in the default blank array)
If you want to update state of the parent component, you should pass down the function from the parent to child component.
Here is very simple example, how to update state with hook from child (Main) component.
With the help of a button from child component you update state of the parent (Application) component.
const Application = () => {
const [todos, setTodos] = useState([]);
const addTodo = message => {
let todosUpdated = [...todos, message];
setTodos(todosUpdated);
};
return (
<>
<Main addTodo={addTodo} />
<pre>{JSON.stringify(todos, null, 2)}</pre>
</>
);
};
const Main = props => {
const input = createRef();
return (
<>
<input type="text" ref={input} />
<button
onClick={() => {
props.addTodo(input.current.value);
input.current.value = "";
}}
>
Add message to state
</button>
</>
);
};
Demo is here: https://codesandbox.io/s/silent-cache-9y7dl
In Application.jsx :
You can pass just a reference to addTodos here. The name on the left can be whatever you want.
<Main addTodos={addTodos} />
In Main.jsx :
Since getTodo returns a Promise, whatever that promise resolves to will be your expected message.
You don't need to pass message as a parameter in Main, just the name of the function.
<Main addTodos={addTodos} />
You are passing addTodos as prop.
<Main
addTodos={message => addTodos(message)}
/>
However, in child component, you are accessing using
props.addTodo(input.current.value);
It should be addTodos.
props.addTodos(input.current.value);

Categories

Resources