Search code examples
reactjsrefrecompose

How to pass event handlers to React-node in React-Recompose App


Got working App at: https://github.com/BeerDRinker/recompose-ref

Following code(commented part in /src/App.js) works as expected:

class App extends Component {
constructor(props) {
    super(props);

    this.node = React.createRef();
    this.state = {
        value: 1
    };
}

handleTouchStart = e => {
    e.preventDefault();
    this.setState({ value: this.state.value + 1 });
};

handleTouchEnd = e => {
    e.preventDefault();
    this.setState({ value: this.state.value - 1 });
};

componentDidMount() {
    this.node.current.ontouchstart = this.handleTouchStart;
    this.node.current.ontouchend = this.handleTouchEnd;
}

render() {
    return (
        <div>
            <h3>Value: {this.state.value}</h3>
            <button ref={this.node}>Submit</button>
        </div>
    );
    }
}

export default App;

But I need the same functionality by using Recompose. I tried, but got nothing working. My code sample(not commented part in /src/App.js) that don't works:

import React from "react";
    import {
        compose,
        lifecycle,
        setDisplayName,
        withProps,
       withStateHandlers
} from "recompose";

import "./App.css";

const state = {
    value: 1
};

const stateHandlers = {
    handleTouchStart: value => () => ({
        value: value + 1
    }),
    handleTouchEnd: value => () => ({
        value: value - 1
    })
};

export const enhance = compose(
    setDisplayName("App"),
    withProps(props => ({
        bookNode: React.createRef()
    })),
    withStateHandlers(state, stateHandlers),
    lifecycle({
        componentDidMount() {
            this.bookNode.current.ontouchstart =   
            this.handleTouchStart;
            this.bookNode.current.ontouchend = this.handleTouchEnd;
        }
    })
);

export const App = ({ value, bookNode }) => (
    <div>
        <h3>Value: {value}</h3>
        <button ref={bookNode}>Submit</button>
    </div>
);

export default enhance(App);

Just start using recompose, lot of things still magic for me )) I hope some on can help me, pass several days to solve this problem.


Solution

  • There are problems in composed component.

    There's no bookNode and event handlers on this. App is stateless component that doesn't have access to this, bookNode and event handlers are props.

    It isn't value that is passed to state handlers, it's state, as the name suggests.

    It should be:

    const stateHandlers = {
        handleTouchStart: state => () => ({
            value: state.value + 1
        }),
        handleTouchEnd: state => () => ({
            value: state.value - 1
        })
    };
    
    export const enhance = compose(
        setDisplayName("App"),
        withProps(props => ({
            bookNode: React.createRef()
        })),
        withStateHandlers(state, stateHandlers),
        lifecycle({
            componentDidMount() {
                this.props.bookNode.current.ontouchstart = this.props.handleTouchStart;
                this.props.bookNode.current.ontouchend = this.props.handleTouchEnd;
            }
        })
    );
    
    export const App = ({ value, bookNode }) => (
        <div>
            <h3>Value: {value}</h3>
            <button ref={bookNode}>Submit</button>
        </div>
    );
    

    Here's a demo.

    Usually there's no reason to access DOM manually to set up events because React handles this. This eliminates the need for a ref and lifecycle hooks:

    export const enhance = compose(
        setDisplayName("App"),
        withStateHandlers(state, stateHandlers)
    );
    
    const App = ({ value, handleTouchStart, handleTouchEnd }) => (
        <div>
            <h3>Value: {value}</h3>
            <button onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd}>Submit</button>
        </div>
    );