markusenglund / react-switch

A draggable toggle-switch component for React. Check out the demo at:
https://react-switch.netlify.com/
MIT License
1.33k stars 101 forks source link

Change checked-state without clicking the switch #91

Closed Doedelpumpe closed 3 years ago

Doedelpumpe commented 3 years ago

Hey guys, i am writing a webapp with nodejs and react. I want to use a switch to send a command to the server to start a function. When the server response with "function is done" i want to refresh the switch-checked-state to false. The communication with the server is working just the refresh of the switch seems to be a problem. I am using a useRef from react to store the .current state an placed it in the checked prop:

<switch checked={checkedRef.current} />

really getting crazy about how to change the checked-state without clicking on the switch. I just started a fresh create-react-app -project with one switch and one button to test things but nothing works. My testcase should just change the switch-checked-state by pressing the seperate button. Sounds like an easy task, please help :)

djD-REK commented 3 years ago

I'm not 100% sure how to accomplish this, but my thought would be to use useState and useEffect.

You can set the useEffect hook to fire when something changes: useEffect(()=>{},[propThatChanges])

or just on initial mount with [] useEffect(()=>{},[])

The propThatChanges you can set up as useState as in [checked, setChecked] = useState(false).

But I'm not sure how you'd persist the "function is done" checking for the serverless function. Maybe you could use a setTimeout to check repeatedly. But I don't think useRef is the right choice here, and it definitely won't achieve what you want by itself.

Best regards, Derek

Derek R. Austin, PT, DPT, MS, BCTMB, LMT, CSCS

Read my blog on Medium: https://medium.com/@DoctorDerek

Join me on LinkedIn: https://linkedin.com/in/Derek-Austin https://www.linkedin.com/in/Derek-Austin/

On Tue, Feb 16, 2021 at 2:25 PM Doedelpumpe notifications@github.com wrote:

Hey guys, i am writing a webapp with nodejs and react. I want to use a switch to send a command to the server to start a function. When the server response with "function is done" i want to refresh the switch-checked-state to false. The communication with the server is working just the refresh of the switch seems to be a problem. I am using a useRef from react to store the .current state an placed it in the checked prop:

really getting crazy about how to change the checked-state without clicking on the switch. I just started a fresh create-react-app -project with one switch and one button to test things but nothing works. My testcase should just change the switch-checked-state by pressing the seperate button. Sounds like an easy task, please help :)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/markusenglund/react-switch/issues/91, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMV42QU7LASFSZIL3XNP5XLS7LBBZANCNFSM4XXAJJOQ .

Doedelpumpe commented 3 years ago

Hey Derek, thanks for your help.

The server sends an socket.io message when the function is done. So the initiation is not the problem.

The problem is a command to change the switch-checked-sate. I need something like:

document.getElementbyId("mySwitch").checked = true;
document.getElementbyId("mySwitch").setchecked(true);

but nothing seems to work.

I need the fox in the box: https://www.youtube.com/watch?v=wlslwdB9Z4g :D

djD-REK commented 3 years ago

Yeah... that's where you would useState to change the state, and the component will re-render if any of its state props change. But I'm not sure exactly how react-switch will handle it in particular.

[checked, setChecked] = useState(false) setChecked(true) // triggers a re-render

Best regards, Derek

Derek R. Austin, PT, DPT, MS, BCTMB, LMT, CSCS

Read my blog on Medium: https://medium.com/@DoctorDerek

Join me on LinkedIn: https://linkedin.com/in/Derek-Austin https://www.linkedin.com/in/Derek-Austin/

On Wed, Feb 17, 2021 at 2:20 AM Doedelpumpe notifications@github.com wrote:

Hey Derek, thanks for your help.

The server sends an socket.io message when the function is done. So the initiation is not the problem.

The problem is a command to change the switch-checked-sate. I need something like:

document.getElementbyId("mySwitch").checked = true; document.getElementbyId("mySwitch").setchecked(true);

but nothing seems to work.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/markusenglund/react-switch/issues/91#issuecomment-780360100, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMV42QVG56TSNDLPTWDUBQ3S7NU5BANCNFSM4XXAJJOQ .

Doedelpumpe commented 3 years ago
const [jukeboxState, setJukeboxState] = useState(false);

class SwitchExample extends Component {
        handleChange() {
            if (jukeboxState === true) {
                setJukeboxState(false);
                JukeboxLoop(false);
            }
            else if (jukeboxState === false) {
                setJukeboxState(true);
                JukeboxLoop(true);
            }
        }
        render() {
            return (
                <label>
                    <span>Switch with default style</span>
                    <Switch onChange={this.handleChange} checked={this.props.checked} />
                </label>
            );
        }
    }

<SwitchExample checked={jukeboxState} />

works now for me somehow. I tried using jukeboxLoop() without a state and the function should have taken the state from the useState but it always used FALSE so i changed it to force it to send the correct state and now it works. The server receives the correct state (like the switch shows) and the switch turns itself off if the function has stopped running on the server.

DoctorDerek commented 3 years ago

Makes sense. Yeah the Hooks can be a little confusing because sometimes they feel a render behind. 

Here's something similar from a project I'm working on.

import { useState, useEffect } from "react"
import { useTheme } from "next-themes"
import { CSSTransition } from "react-transition-group"

const ThemeSwitch = () => {
  const [inProp, setInProp] = useState(false)
  const { resolvedTheme, systemTheme, setTheme } = useTheme()
  /*  useTheme takes no parameters, but returns:
      theme: Active theme name

      setTheme(name): Function to update the theme

      forcedTheme: Forced page theme or falsy. If forcedTheme is set, you should disable any theme switching UI

      resolvedTheme: If enableSystem is true and the active theme is "system", this returns whether the system preference resolved to "dark" or "light". Otherwise, identical to theme

      systemTheme: If enableSystem is true, represents the System theme preference ("dark" or "light"), regardless what the active theme is
      themes: The list of themes passed to ThemeProvider (with "system" appended, if enableSystem is true) */

  // Transition "in" prop from false to true after mounting to show
  // dark toggle correctly when refreshing with dark mode enabled
  useEffect(() => {
    if (resolvedTheme === "dark") setInProp(true)
  }, [resolvedTheme])

Since resolvedTheme isn't, well, resolved by the first render then I have to update the prop to toggle dark mode when it resolves. That's why the useEffect hook is waiting on [resolvedTheme] and then triggering the useState hook.

Doedelpumpe commented 3 years ago

The behaviour is sometimes weird yes. I placed console.log(jukeboxState)

The result was:

handleChangeEntry:  false // the Switch was off, so everythings correct so far
SwitchStateChanger:  true // the setJukeboxState(true) hit the program, the switch turned on
handleChange:  false // What is this? direct after setting the state to true, the log says false again. Is it lagging behind?
// The server receives false and returns false
SwitchStateChanger:  false // the switch moves back to false

As i used jukeboxLoop() without any argument and turned off the answer of the server, jukeboxLoop sent false but the switch stayed on (and logged true). After clicking again the switch sent true but stayed off (log: false). So it is lagging one step behind... I think it would be cooler to point everything to the jukeboxState and use it as much as possible but for now i think i have to stick to sending the correct argument to the jukeboxLoop().

DoctorDerek commented 3 years ago

Yeah it lags behind because it's asynchronous. I don't want to confuse myself wrapping my brain around it without hooks, but this is a common issue. Basically the state isn't what you expect.

You fix it by passing it a callback function to useState if you think you may have this type of problem.

[checked, setChecked] = useState(false) handleChange = () => setChecked(!checked) // could be be buggy handleChange = () => setChecked((checked) => setChecked(!checked))

The local version inside the callback function is guaranteed to be the correct one.

This article discusses it https://kentcdodds.com/blog/use-state-lazy-initialization-and-function-updates and has a demo.

and I wrote about it in passing here: https://medium.com/javascript-in-plain-english/how-to-add-to-an-array-in-react-state-3d08ddb2e1dc

Best regards, Derek

Derek R. Austin, PT, DPT, MS, BCTMB, LMT, CSCS

Read my blog on Medium: https://medium.com/@DoctorDerek

Join me on LinkedIn: https://linkedin.com/in/Derek-Austin https://www.linkedin.com/in/Derek-Austin/

On Wed, Feb 17, 2021 at 4:24 PM Doedelpumpe notifications@github.com wrote:

The behaviour is sometimes weird yes. I placed console.log(jukeboxState)

  • in front of the Switch
  • at the beginning of handleChange()
  • after setJukeboxState(true) in handleChange()

The result was: handleChangeEntry: false // the Switch was off, so everythings correct so far SwitchStateChanger: true // the setJukeboxState(true) hit the program, the switch turned on handleChange: false // What is this? direct after setting the state to true, the log says false again. Is it lagging behind? // The server receives false and returns false SwitchStateChanger: false // the switch moves back to false

As i used jukeboxLoop() without any argument and turned off the answer of the server, jukeboxLoop sent false but the switch stayed on (and logged true). After clicking again the switch sent true but stayed off (log: false). So it is lagging one step behind... I think it would be cooler to point everything to the jukeboxState and use it as much as possible but for now i think i have to stick to sending the correct argument to the jukeboxLoop().

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/markusenglund/react-switch/issues/91#issuecomment-780863950, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFZ3D4DYD43NZXYFKKZX4LS7QX2DANCNFSM4XXAJJOQ .

markusenglund commented 3 years ago

Thanks for helping with the issue @DoctorDerek. Closing.

DoctorDerek commented 3 years ago

👍

On Sun, Mar 14, 2021, 6:52 AM Markus Englund @.***> wrote:

Thanks for helping with the issue @DoctorDerek https://github.com/DoctorDerek. Closing.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/markusenglund/react-switch/issues/91#issuecomment-798893906, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFZ3DZBDJMCMQKBZIKAF53TDSPRTANCNFSM4XXAJJOQ .