javascriptreactjsreact-hooksseparation-of-concerns

Reactjs separation of UI and business logic


I am new to react and I find it sore in the eyes to look at the component flooded with lots of functions and variable initializations together with the UI. Is it possible to separate them?

Instead of the default setup, like below. How do I separate the business logic into another file?

function MyComponent() {
    const [data, setData] = useState('');
    const someFunc = () => {
        //do something.
    };
    ... some 100-liner initializations

   return ( 
       ...
   )
}

Solution

  • Yes it is possible, That is called as Separation of concern.

    You can create your component structure as below.

    MyComponentDirectory
     - useCustomHook
     - Component
     - helper
    

    The code will look like this one.

    Hook

    const useCustomHook = () => {
    
        const [value, setValue] = useState('');
        const handleClick = (value) => {
            setValue(value)
            //do something.
        };
        ... some 100-liner initializations/business logic, states, api calls. 
    
        return {
            value, 
            handleClick,
            ... // Other exports you need. 
        } 
    }
    
    export default useCustomHook; 
    

    Component

    function MyComponent() {
       const {
           value, 
           handleClick, 
           ... // Other imports 
       } = useCustomHook() 
    
       return ( 
           <Element value={value} onClick={handleClick} />
       )
    }
    
    

    Helper

    const doSomething = () => {}
    

    EDIT

    Here's a detailed example of React counter application using Separation of concern

    Structure

    Directory
    - App
    - Counter
    - useCounter
    - helper
    

    App Component

    import Counter from "./Counter";
    import "./styles.css";
    
    export default function App() {
      return (
        <div className="App">
          <Counter />
        </div>
      );
    }

    Counter Component

    import useCounter from "./useCounter";
    
    const Counter = () => {
      const { count, increaseCount, decreaseCount } = useCounter();
    
      return (
        <div>
          <p>{count}</p>
          <div>
            <button onClick={increaseCount}>Increase</button>
            <button onClick={decreaseCount}>Decrease</button>
          </div>
        </div>
      );
    };
    
    export default Counter;

    useCounter Hook

    import { useState } from "react";
    import numberWithCommas from "./helper";
    
    const useCounter = () => {
      const [count, setCount] = useState(9999);
    
      const increaseCount = () => setCount(count + 1);
      const decreaseCount = () => setCount(count - 1);
    
      return {
        count: numberWithCommas(count),
        increaseCount,
        decreaseCount
      };
    };
    
    export default useCounter;

    Helper Function

    const numberWithCommas = (x) => {
      return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    };
    
    export default numberWithCommas;

    Here's the working example in Codesandbox

    Note: if you create a simple Javascript util function instead of hook then you won't be able to access other hooks, context inside that function.