Search code examples
javascriptreactjsconventions

How to inject functions that change component state?


I want to keep some functions outside of my component for easier testing. However, I cannot change state with these functions because they cannot reference the component's state directly.

So I currently have the hacky solution where I set the function to a variable then call this.setState. Is there a better convention/more efficient way to do this?

Example function code in Tester.js:

const tester = () => {
  return 'new data';
}

export default tester;

Example component code in App.js (without imports):

class App extends Component {
    constructor() {
        super();
        this.state = {
            data: ''
        }
    }

    componentDidMount(){
        let newData = tester();
        this.setState({ data: newData })
    }

    render() {
        return(
            <div>{this.state.data}</div>
        )
    }
}

Solution

  • You could bind your tester function like this (this approach doesn't work with arrow functions):

    function tester() {
      this.setState({ data: 'new Data' });
    }
    
    class App extends Component {
      constructor() {
        super();
        this.state = {
          data: '',
        };
        this.tester = tester.bind(this);
      }
    
      componentDidMount() {
        this.tester();
      }
    
      render() {
        return (
          <div>{this.state.data}</div>
        );
      }
    }
    

    But I would prefer a cleaner approach, where you don't need your function to access this (also works with arrow functions):

    function tester(prevState, props) {
      return {
        ...prevState,
        data: 'new Data',
      };
    }
    
    class App extends Component {
      constructor() {
        super();
        this.state = {
          data: '',
        };
      }
    
      componentDidMount() {
        this.setState(tester);
      }
    
      render() {
        return (
          <div>{this.state.data}</div>
        );
      }
    }