rjwats / esp8266-react

A framework for ESP8266 & ESP32 microcontrollers with a React UI
GNU Lesser General Public License v3.0
478 stars 147 forks source link

How to use a Slider component #223

Closed jaccobezemer closed 3 years ago

jaccobezemer commented 3 years ago

Hello,

I need a Slider to change the speed of a fan and changed the Switch in LightStateWebSocketController.tsx of the demo project into a Slider. Unfortunately I can't get it to work. It seems that the Slider's onChange isn't fired. This is my code:

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    console.log(event.target.value);
    setData({ speed: event.target.valueAsNumber }, saveData);
  }
      <BlockFormControlLabel
        control={
          <Slider
            value={typeof data.speed === 'number' ? data.speed : 0}
            valueLabelDisplay="auto"
            onChange={() => handleChange}
          />
        }
        label="LED State?"
      />

How can I get this to work?

Thanks in advance

rjwats commented 3 years ago

Sure,

Here's an example of a working slider component:

https://github.com/rjwats/esp8266-react/blob/audio-led2/interface/src/project/LedSettingsForm.tsx

On Sat, Jan 23, 2021 at 9:53 PM jaccobezemer notifications@github.com wrote:

Hello,

I need a Slider to change the speed of a fan and changed the Switch in LightStateWebSocketController.tsx of the demo project into a Slider. Unfortunately I can't get it to work. It seems that the Slider's onChange isn't fired. This is my code:

const handleChange = (event: React.ChangeEvent) => { console.log(event.target.value); setData({ speed: event.target.valueAsNumber }, saveData); } <BlockFormControlLabel control={ <Slider value={typeof data.speed === 'number' ? data.speed : 0} valueLabelDisplay="auto" onChange={() => handleChange} /> } label="LED State?" />

How can I get this to work?

Thanks in advance

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rjwats/esp8266-react/issues/223, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAKE4VDL4XBRMEAMEWYP5K3S3NANJANCNFSM4WQACW2Q .

rjwats commented 3 years ago

In your particular instance, the problem may be that you are using a lambda function as your onChange handler. The lambda will be called with the onChange event and it will return the function you are actually trying to invoke (hence you are observing it not being called)..

The example above may be useful still if this doesn't work, but it's probably as simple as changing waht you are passing into your onChange prop as follows:

<Slider
  value={typeof data.speed === 'number' ? data.speed : 0}
  valueLabelDisplay="auto"
  onChange={handleChange}
/>
jaccobezemer commented 3 years ago

Hi rjwats,

Thanks for your reaction.

I'm not a web designer (that's why i'm using your framework;-) so it's not that simple for me:-) I already tried your suggestion onChange={handleChange} but this results in a compile error: Type '(event: React.ChangeEvent) => void' is not assignable to type '(event: ChangeEvent<{}>, value: number | number[]) => void'.

And for the working example, I can't factor this in the LightStateWebSocketController.tsx code. I get error: 'this' implicitly has type 'any' because it does not have a type annotation.

So i'm still kinda lost.

rjwats commented 3 years ago

Can you send the complete file please?

rjwats commented 3 years ago

Try the following for your on change function:

  const handleChange = (event: React.ChangeEvent<{}>, value: number | number[]) => {
    setData({ speed: value as number }, saveData);
  }

Looks like slider can return an array of numbers or a single number, let's tell TypeScript to expect a number here. Here is what my file looks like:

import React, { Component } from 'react';
import { ValidatorForm } from 'react-material-ui-form-validator';

import { FormLabel, Slider } from '@material-ui/core';
import { WEB_SOCKET_ROOT } from '../api';
import { WebSocketControllerProps, WebSocketFormLoader, WebSocketFormProps, webSocketController } from '../components';
import { SectionContent } from '../components';

import { FanState } from './types';

export const FAN_STATE_WEBSOCKET_URL = WEB_SOCKET_ROOT + "fanState";

type FanStateWebSocketControllerProps = WebSocketControllerProps<FanState>;

class FanStateWebSocketController extends Component<FanStateWebSocketControllerProps> {

  render() {
    return (
      <SectionContent title='WebSocket Controller' titleGutter>
        <WebSocketFormLoader
          {...this.props}
          render={props => (
            <FanStateWebSocketControllerForm {...props} />
          )}
        />
      </SectionContent>
    )
  }

}

export default webSocketController(FAN_STATE_WEBSOCKET_URL, 100, FanStateWebSocketController);

type FanStateWebSocketControllerFormProps = WebSocketFormProps<FanState>;

function FanStateWebSocketControllerForm(props: FanStateWebSocketControllerFormProps) {
  const { data, saveData, setData } = props;

  const handleChange = (event: React.ChangeEvent<{}>, value: number | number[]) => {
    setData({ speed: value as number }, saveData);
  }

  return (
    <ValidatorForm onSubmit={saveData}>
      <FormLabel>Fan Speed</FormLabel>
      <Slider
        value={data.speed}
        valueLabelDisplay="auto"
        onChange={handleChange}
      />
    </ValidatorForm>
  );
}
jaccobezemer commented 3 years ago

Great! This const handleChange = (event: React.ChangeEvent<{}>, value: number | number[]) => { setData({ speed: value as number }, saveData); } did the trick. Many thanks.

rjwats commented 3 years ago

No worries, happy to help.