How to divide business logic in one react component?

Viewed 7907

i use react.js to build my spa app. I use functional style to make my components. As the business logic gonna bigger, there are inevitably many functions. So i tried to divide in to multiple components. Because it's hard to put many codes in one file even if it is just a 1 component.

However, this also has obvious limitations. In the case of complex components, there are a large number of event callback functions in addition to the functions directly called by the user. Depending on the size of the component, it is sometimes difficult to write all the logic in one jsx file, so I want to divide the code into different files as needed. (Like c# partial class)

However, this is not easy. As an example, let's assume that the callback functions are made external functions of other files and imported into this component jsx file and used. But it seems that the component states, props information and the dispatch function also should be passed as parameters to the function. This seems hassle but except this, i have no idea a way to access this component's states, props, dispatch function from a function in another file.)

//For example of callback function
const onHoldButtonClicked = (state, props, dispatch, event) =>
{
    ~~~
}
//For example of normal function
const updateValidUser = (state, props, dispatch, userInfo, data) =>
{
    let id = userInfo.id;
    if(id == data.passID)
    {
         if(props.valid == 10)
            dispatch({action: 'ChangeUser', user: id});
    }
}

In React, how to divide logic(functions) when the logic gonna bigger in one component? (In general case) Even if it is divided into several components, a big component inevitably has many functions.

4 Answers

I would recommend to extract logic into hooks and place these hooks into their own files.

hook.js

const useAwesomeHook = () => {
  const [someState, setSomeState] = useState("default");

  const myCoolFunction = useCallback(() => {
    console.log('do smth cool', someState);
  }, [someState]);

  return myCoolFunction;
};

export default useAwesomeHook;

main.js

import useAwesomeHook from './hook';

const Main = ({ someProperty }) => {
  const myCoolFunction = useAwesomeHook(someProperty);

  return <button onClick={myCoolFunction}>Click me</button>;
};

Here is an example for logic and business and component separation. The separation makes your code testable, atomic, maintainable, readable and SRP(single responsibility rule )

 // Presentational component
    const QuantitySelector = () => {
      const { onClickPlus, onClickMinus, state } = useQuantitySelector();
      const { message, value } = state;
      return (
        <div className="quantity-selector">
          <button onClick={onClickMinus} className="button">
            -
          </button>
          <div className="number">{value}</div>
          <button onClick={onClickPlus} className="button">
            +
          </button>
          <div className="message">{message}</div>
        </div>
      );
    };
    
    export default QuantitySelector;

and below code is the above component logic

import { useState } from "react";

// Business logic. Pure, testable, atomic functions
const increase = (prevValue, max) => {
  return {
    value: prevValue < max ? prevValue + 1 : prevValue,
    message: prevValue < max ? "" : "Max!"
  };
};

const decrease = (prevValue, min) => {
  return {
    value: prevValue > min ? prevValue - 1 : prevValue,
    message: prevValue > min ? "" : "Min!"
  };
};

// Implementation/framework logic. Encapsulating state and effects here
const useQuantitySelector = () => {
  const [state, setState] = useState({
    value: 0,
    message: ""
  });
  const onClickPlus = () => {
    setState(increase(state.value, 10));
  };
  const onClickMinus = () => {
    setState(decrease(state.value, 0));
  };
  return { onClickPlus, onClickMinus, state };
};

export default useQuantitySelector;

I separate components into the logic and UI by function composition.The idea came from recompse which I used before hooks came into react. you can add two helper functions. first one is compose you can learn more about it here:

    const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);
    export default compose;

the second one is withHooks:

    import React from 'react';
    export default (hooks) =>
    (WrappedComponent) =>
    (props) => {
        const hookProps = hooks(props);

        return (
            <WrappedComponent
                {...props}
                {...hookProps}
            />
        );
    }

with these two functions, you can put your logic in the hooks file and pass the as props to your UI with compose file you can see sandbox example here

what I usually do is create a folder for the big component, in the folder I create a functions file and put functions with state passed and other params necessary . as simple as that .

export const increment=(count,setCount)=>{...}
.
.

and in your component

import{increment,....} from './functions'
const Component=(props)=>{
  const [count,setCount]=useState(1)
  return <div>
    <button onClick={e=>increment(count,setCount)}> count ={count}</button>
</div>
}
Related