我是 React 新手,看着充满大量函数和变量初始化的组件以及 UI,我觉得眼睛很痛。可以分开吗?
而不是默认设置,如下所示。如何将业务逻辑分离到另一个文件中?
function MyComponent() {
const [data, setData] = useState('');
const someFunc = () => {
//do something.
};
... some 100-liner initializations
return (
...
)
}
是的,这是可能的,这就是所谓的
Separation of concern
。
您可以如下创建组件结构。
MyComponentDirectory
- useCustomHook
- Component
- helper
代码看起来像这样。
挂钩
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;
组件
function MyComponent() {
const {
value,
handleClick,
... // Other imports
} = useCustomHook()
return (
<Element value={value} onClick={handleClick} />
)
}
帮手
const doSomething = () => {}
编辑
这是使用
Separation of concern
的 React 计数器应用程序的详细示例
结构
Directory
- App
- Counter
- useCounter
- helper
应用程序组件
import Counter from "./Counter";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Counter />
</div>
);
}
计数器组件
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;
使用反钩
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;
辅助功能
const numberWithCommas = (x) => {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
export default numberWithCommas;
这是 Codesandbox
中的工作示例注意:如果您创建一个简单的 Javascript util 函数而不是钩子,那么您将无法访问其他钩子以及该函数内的上下文。
我自己使用的一种常见方法是将业务逻辑分离到自己的文件中
myComponentHelper.js
这也将使测试函数变得更容易,因为如果不将其作为参数传入并返回更改,它将无法使用和更改反应状态。
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");
});
可以通过各种不同的方式将业务逻辑分离到其他文件中。
根据单一职责原则,使用自定义挂钩以简单的方式保持业务逻辑(如果需要,请在其中使用原型继承),要存储API调用,请使用Tanstack Query并使用Jotai(Atoms)存储全局数据。这个库非常容易学习和维护。
您现在不需要编写 Redux(action、reducers 和 store)、Redux 工具包和其他样板代码。即使您学习了这个概念,它在其他堆栈中也没有那么有用。
下面给出了示例片段。
const Counter = () => {
const {sendCounterdata} = useCounterAPI() // TanstackQuery
const [counter, setCounter] = useAtom(counterAtom); // Jotai atom
// Custom hook
const { increment, decrement, submit } =
useCounter({setCounter, onSubmit: sendCounterdata});
return (
<>
{counter}
<Button onPress={increment}>Increment</Button>
<Button onPress={decrement}>Decrement</Button>
<Button onPress={submit}>Submit</Button>
</>
);
};
注意:React 团队建议对业务逻辑使用自定义挂钩而不是 HOC。
历史:React mixins => Redux Redcers => HOC => React hooks。
奖励:您可以轻松地为自定义挂钩“useCounter”编写单元测试。