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 (
...
)
}
CodePudding user response:
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.
CodePudding user response:
A common approach that I use myself is to separate the business logic into its own file myComponentHelper.js
This will also make it easier to test the function because it will not be able to use and change the react state without having it passed in as arguments and returning the changes.
myComponent/
myComponent.jsx
myComponentHelper.js
myComponentTest.js
// myComponent.js
import { someFunc } from './myComponentHelper';
function MyComponent() {
const [data, setData] = useState('');
const x = someFunc(data);
return (
...
)
}
// myComponentHelper.js
export const someFunc = (data) => {
//do something.
return something;
}
// myComponentTest.js
import { someFunc } from './myComponentHelper';
test("someFunc - When data is this - Should return this", () => {
const data = {...};
const result = someFunc(data);
expect(result).toEqual("correct business data");
});
CodePudding user response:
Separating business logic into other files can be done in various different ways.
- Create a helperFile.js that has logic or basically the functions required by the corresponding file.
- Creating Custom Hooks. More on that can be found here in the official docs or in this playlist (refer the videos at the very end)
- Global State mangement way - where contextAPI or Redux is used to seperate out state and business logic