React Hooks - can't modify state











up vote
1
down vote

favorite












I'm trying to refactor a class into a stateless component using React hooks.



The component itself is very simple and I don't see where I'm making a mistake, as it's almost a copy paste from the react docs.



The component is showing a popup when the user clicks on a button (button is passed through props to my component). I'm using typescript.



I commented the line that fails to do what I want in the hooks version



Here's my original class:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export interface NodeMenuState {
visible: boolean
}
export class NodeMenu extends React.Component<NodeMenuProps, NodeMenuState> {
state = {
visible: false
}

hide = () => {
this.setState({
visible: false
})
}

handleVisibleChange = (visible: boolean) => {
this.setState({ visible })
}

render() {
return (
<div className={this.props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={this.props.content}
title={this.props.title}
trigger="click"
placement="bottom"
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange}
>
{this.props.button}
</Popover>
</div>
</div>
)
}
}


Here's the React hooks version:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
console.log(isVisible) // is always `false` despite `visible` being true.
}

return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}









share|improve this question
























  • visible is a constant defined in your component. There is no possible way that calling any functions could change its value.
    – Dan Abramov
    Nov 22 at 12:21










  • Instead, setVisible triggers a re-render. When we render again, it will be set to a new value.
    – Dan Abramov
    Nov 22 at 12:22

















up vote
1
down vote

favorite












I'm trying to refactor a class into a stateless component using React hooks.



The component itself is very simple and I don't see where I'm making a mistake, as it's almost a copy paste from the react docs.



The component is showing a popup when the user clicks on a button (button is passed through props to my component). I'm using typescript.



I commented the line that fails to do what I want in the hooks version



Here's my original class:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export interface NodeMenuState {
visible: boolean
}
export class NodeMenu extends React.Component<NodeMenuProps, NodeMenuState> {
state = {
visible: false
}

hide = () => {
this.setState({
visible: false
})
}

handleVisibleChange = (visible: boolean) => {
this.setState({ visible })
}

render() {
return (
<div className={this.props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={this.props.content}
title={this.props.title}
trigger="click"
placement="bottom"
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange}
>
{this.props.button}
</Popover>
</div>
</div>
)
}
}


Here's the React hooks version:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
console.log(isVisible) // is always `false` despite `visible` being true.
}

return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}









share|improve this question
























  • visible is a constant defined in your component. There is no possible way that calling any functions could change its value.
    – Dan Abramov
    Nov 22 at 12:21










  • Instead, setVisible triggers a re-render. When we render again, it will be set to a new value.
    – Dan Abramov
    Nov 22 at 12:22















up vote
1
down vote

favorite









up vote
1
down vote

favorite











I'm trying to refactor a class into a stateless component using React hooks.



The component itself is very simple and I don't see where I'm making a mistake, as it's almost a copy paste from the react docs.



The component is showing a popup when the user clicks on a button (button is passed through props to my component). I'm using typescript.



I commented the line that fails to do what I want in the hooks version



Here's my original class:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export interface NodeMenuState {
visible: boolean
}
export class NodeMenu extends React.Component<NodeMenuProps, NodeMenuState> {
state = {
visible: false
}

hide = () => {
this.setState({
visible: false
})
}

handleVisibleChange = (visible: boolean) => {
this.setState({ visible })
}

render() {
return (
<div className={this.props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={this.props.content}
title={this.props.title}
trigger="click"
placement="bottom"
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange}
>
{this.props.button}
</Popover>
</div>
</div>
)
}
}


Here's the React hooks version:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
console.log(isVisible) // is always `false` despite `visible` being true.
}

return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}









share|improve this question















I'm trying to refactor a class into a stateless component using React hooks.



The component itself is very simple and I don't see where I'm making a mistake, as it's almost a copy paste from the react docs.



The component is showing a popup when the user clicks on a button (button is passed through props to my component). I'm using typescript.



I commented the line that fails to do what I want in the hooks version



Here's my original class:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export interface NodeMenuState {
visible: boolean
}
export class NodeMenu extends React.Component<NodeMenuProps, NodeMenuState> {
state = {
visible: false
}

hide = () => {
this.setState({
visible: false
})
}

handleVisibleChange = (visible: boolean) => {
this.setState({ visible })
}

render() {
return (
<div className={this.props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={this.props.content}
title={this.props.title}
trigger="click"
placement="bottom"
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange}
>
{this.props.button}
</Popover>
</div>
</div>
)
}
}


Here's the React hooks version:



export interface NodeMenuProps extends PropsNodeButton {
title?: string
content?: JSX.Element
button?: JSX.Element
}
export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
console.log(isVisible) // is always `false` despite `visible` being true.
}

return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}






reactjs typescript react-hooks






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 22 at 12:03









Shubham Khatri

75.9k1386126




75.9k1386126










asked Nov 22 at 9:58









Greg Forel

111211




111211












  • visible is a constant defined in your component. There is no possible way that calling any functions could change its value.
    – Dan Abramov
    Nov 22 at 12:21










  • Instead, setVisible triggers a re-render. When we render again, it will be set to a new value.
    – Dan Abramov
    Nov 22 at 12:22




















  • visible is a constant defined in your component. There is no possible way that calling any functions could change its value.
    – Dan Abramov
    Nov 22 at 12:21










  • Instead, setVisible triggers a re-render. When we render again, it will be set to a new value.
    – Dan Abramov
    Nov 22 at 12:22


















visible is a constant defined in your component. There is no possible way that calling any functions could change its value.
– Dan Abramov
Nov 22 at 12:21




visible is a constant defined in your component. There is no possible way that calling any functions could change its value.
– Dan Abramov
Nov 22 at 12:21












Instead, setVisible triggers a re-render. When we render again, it will be set to a new value.
– Dan Abramov
Nov 22 at 12:22






Instead, setVisible triggers a re-render. When we render again, it will be set to a new value.
– Dan Abramov
Nov 22 at 12:22














1 Answer
1






active

oldest

votes

















up vote
1
down vote













Much like setState, the state update behaviour using hooks will also require a re-render and update and hence the change will not be immedialtely visible. If however you try to log state outside of the handleVisibleChange method, you will see the update state



export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
}

console.log({ isVisible });
return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}


Any action that you need to take on the basis of whether the state was update can be done using the useEffect hook like



useEffect(() => {
// take action when isVisible Changed
}, [isVisible])





share|improve this answer





















  • Thanks Shubham, I can see the value updating now. However, since isVisible becomes true at re-render, I don't understand why the popover doesn't show up. I read the docs about useEffect, but I can't see why I need to use this for the popup to show up since isVisible does change to true?
    – Greg Forel
    Nov 22 at 15:32










  • you dont need to use useEffect. That was just an additional information
    – Shubham Khatri
    Nov 22 at 16:05











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53428291%2freact-hooks-cant-modify-state%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote













Much like setState, the state update behaviour using hooks will also require a re-render and update and hence the change will not be immedialtely visible. If however you try to log state outside of the handleVisibleChange method, you will see the update state



export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
}

console.log({ isVisible });
return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}


Any action that you need to take on the basis of whether the state was update can be done using the useEffect hook like



useEffect(() => {
// take action when isVisible Changed
}, [isVisible])





share|improve this answer





















  • Thanks Shubham, I can see the value updating now. However, since isVisible becomes true at re-render, I don't understand why the popover doesn't show up. I read the docs about useEffect, but I can't see why I need to use this for the popup to show up since isVisible does change to true?
    – Greg Forel
    Nov 22 at 15:32










  • you dont need to use useEffect. That was just an additional information
    – Shubham Khatri
    Nov 22 at 16:05















up vote
1
down vote













Much like setState, the state update behaviour using hooks will also require a re-render and update and hence the change will not be immedialtely visible. If however you try to log state outside of the handleVisibleChange method, you will see the update state



export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
}

console.log({ isVisible });
return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}


Any action that you need to take on the basis of whether the state was update can be done using the useEffect hook like



useEffect(() => {
// take action when isVisible Changed
}, [isVisible])





share|improve this answer





















  • Thanks Shubham, I can see the value updating now. However, since isVisible becomes true at re-render, I don't understand why the popover doesn't show up. I read the docs about useEffect, but I can't see why I need to use this for the popup to show up since isVisible does change to true?
    – Greg Forel
    Nov 22 at 15:32










  • you dont need to use useEffect. That was just an additional information
    – Shubham Khatri
    Nov 22 at 16:05













up vote
1
down vote










up vote
1
down vote









Much like setState, the state update behaviour using hooks will also require a re-render and update and hence the change will not be immedialtely visible. If however you try to log state outside of the handleVisibleChange method, you will see the update state



export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
}

console.log({ isVisible });
return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}


Any action that you need to take on the basis of whether the state was update can be done using the useEffect hook like



useEffect(() => {
// take action when isVisible Changed
}, [isVisible])





share|improve this answer












Much like setState, the state update behaviour using hooks will also require a re-render and update and hence the change will not be immedialtely visible. If however you try to log state outside of the handleVisibleChange method, you will see the update state



export const NodeMenu: React.SFC<NodeMenuProps> = props => {
const [isVisible, setIsVisible] = useState(false)

const hide = () => {
setIsVisible(false)
}

const handleVisibleChange = (visible: boolean) => {
console.log(visible) // visible is `true` when user clicks. It works
setIsVisible(visible) // This does not set isVisible to `true`.
}

console.log({ isVisible });
return (
<div className={props.className}>
<div className={styles.requestNodeMenuIcon}>
<Popover
content={props.content}
title={props.title}
trigger="click"
placement="bottom"
visible={isVisible}
onVisibleChange={handleVisibleChange}
>
{props.button}
</Popover>
</div>
</div>
)
}


Any action that you need to take on the basis of whether the state was update can be done using the useEffect hook like



useEffect(() => {
// take action when isVisible Changed
}, [isVisible])






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 22 at 11:38









Shubham Khatri

75.9k1386126




75.9k1386126












  • Thanks Shubham, I can see the value updating now. However, since isVisible becomes true at re-render, I don't understand why the popover doesn't show up. I read the docs about useEffect, but I can't see why I need to use this for the popup to show up since isVisible does change to true?
    – Greg Forel
    Nov 22 at 15:32










  • you dont need to use useEffect. That was just an additional information
    – Shubham Khatri
    Nov 22 at 16:05


















  • Thanks Shubham, I can see the value updating now. However, since isVisible becomes true at re-render, I don't understand why the popover doesn't show up. I read the docs about useEffect, but I can't see why I need to use this for the popup to show up since isVisible does change to true?
    – Greg Forel
    Nov 22 at 15:32










  • you dont need to use useEffect. That was just an additional information
    – Shubham Khatri
    Nov 22 at 16:05
















Thanks Shubham, I can see the value updating now. However, since isVisible becomes true at re-render, I don't understand why the popover doesn't show up. I read the docs about useEffect, but I can't see why I need to use this for the popup to show up since isVisible does change to true?
– Greg Forel
Nov 22 at 15:32




Thanks Shubham, I can see the value updating now. However, since isVisible becomes true at re-render, I don't understand why the popover doesn't show up. I read the docs about useEffect, but I can't see why I need to use this for the popup to show up since isVisible does change to true?
– Greg Forel
Nov 22 at 15:32












you dont need to use useEffect. That was just an additional information
– Shubham Khatri
Nov 22 at 16:05




you dont need to use useEffect. That was just an additional information
– Shubham Khatri
Nov 22 at 16:05


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53428291%2freact-hooks-cant-modify-state%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

What visual should I use to simply compare current year value vs last year in Power BI desktop

Alexandru Averescu

Trompette piccolo