回应 メモ
目录
- src
// プロジェクトによって変えればok
// 各componentはContainerとPresentationで分けるとPresentationはpropsを利用すればいいだけになる
- components // 以下各ディレクトリで使うもの
- index.ts // ContainerComponetをexport
- hooks.ts // ロジックを分離, テストできるようにする, useDispatch, useStateなど
- ${component名}Container.tsx // hooksやpropsを受け取り${component名}.tsxに渡す
- ${component名}.tsx // Presentational Component
- enums
- consts
- hooks // 共通hooks定義
- models
- ${domain名.ts}
- redux
- ${domain名}
- actions.ts // thunkのみ
- index.ts // adapter, initialState, reducer, selector, Stateの型, actions(actionとthunkActionをマージ)をexport
- module.ts // slice
- selectors.ts
- types.ts // actionのname spaceとthunkの型をexport
- router
- index.tsx // 各pagesへのrouter定義, lazyで読み込んだほうが軽い
- AuthRouterなど
- repositories
- utils
型別定義(TypeScript)
利用下面的定义来扩展上面的类型,可以更容易地进行修正和扩展。
儲存庫層
-
- apiのデータを表示用に加工してしまうとstoreにその値が反映されてしまう
storeには純粋なデータを保持、各セレクターで取得時に加工するなど
错误处理
-
- apiClientなどでエラーハンドリングして加工してから再スローすると、他の箇所でエラーハンドリングの処理が書きやすい
-
- エラーハンドリングの処理を共通化できるもの
reduxのactionで書いちゃう
分岐するもの
hooks内でそれぞれハンドリング処理を書き、Componentで呼び出しするとよい
unwrapを使うとthunkの結果をそのまま受け取れる
thunk内でのエラーのスローも、rejectedWithValueもtry-catchで受けれる
try {
await dispatch(todoActions.getTodosAsync()).unwrap()
} catch (e) {
// error handling
}
在Thunk内发生错误时的操作行为
-
- エラーをスローした場合
reducerのactionにはerrorに値が格納されてくる
rejectedWithValueをreturnした場合
reducerのactionにはpayloadに値が格納されてくる
使用效果
- 早期return
请用中文重新表达以下内容,只需要一种选项:
提到
state参与的钩子
如果在自定义Hooks中需要更新其他Hooks的状态。
- state更新をする関数を渡してあげないとダメ
const useCount = () => {
const [count, setCount] = useState(0)
}
更迭、重新出现
请把以下内容用中文进行本地化改写,只需要一种选项:
类型
行动
关于Action的Payload类型
const counterSlice = createSlice({
// ...
reducers: {
incrementByAmount(state, action) {
// ...
},
},
})
export const { incrementByAmount } = counterSlice.actions
type IncrementByAmountParam = ReturnType<typeof incrementByAmount>['payload']
思考
導出型定義
选择器 qì)
-
- stateの参照のみはselectorの切り出しをしないでいい
第二引数にfast-deep-equalを利用して再レンダリングを回避
https://www.npmjs.com/package/fast-deep-equal
*ただし、state参照の処理自体はかかる
テストも不要
ロジック含む、配列処理(map, filter)などは検証や重い処理になってしまう場合があるのでselectorに切り出し
createSelectorを利用
inputSelectorsが返す値が前回と同じだった場合は、resultFunction を呼ぶことなく、キャッシュした前回の値を返す
キャッシュ数は1
https://9oelm.github.io/2020-09-13–如何使useSelector不成为灾难/
https://scrapbox.io/mrsekut-p/createSelector
https://scrapbox.io/mrsekut-p/useSelector
减少案例
以下是两个网址链接:
1. https://redux-toolkit.js.org/api/createSlice#examples
2. https://stackoverflow.com/questions/67792374/react-redux-alerting-best-practices
Translation:
以下是两个网址链接:
1. https://redux-toolkit.js.org/api/createSlice#examples
2. https://stackoverflow.com/questions/67792374/react-redux-alerting-best-practices
在reducer内部可以调用其他reducer。
reducers: {
addTodo: (state, action: PayloadAction<Omit<Todo, 'id' | 'isDone'>>) => {
const id =
state.todos.map((todo) => todo.id).reduce((a, b) => Math.max(a, b), 0) +
1
const updatedTodo = { ...action.payload, id, isDone: false }
const updatedTodos = state.todos.concat(updatedTodo)
state.todos = updatedTodos
const deleteTodoAction = slice.actions.deleteTodo(1)
slice.caseReducers.deleteTodo(state, deleteTodoAction) // ここ
},
此外,还可以调用其他切片的动作。并且,可以在reducer层面处理uiState的loading等操作,而不是由组件处理,以实现共享处理。
const slice = createSlice({
name: 'ui',
initialState: {
loading: false,
},
reducers: {
updateLoading: (state, action: PayloadAction<{ loading: boolean }>) => {
state.loading = action.payload.loading
},
},
extraReducers: (builder) => {
builder
.addCase(todoActions.getTodosAsync.pending, (state, _action) => {
const action = slice.actions.updateLoading({ loading: true })
slice.caseReducers.updateLoading(state, action)
})
.addMatcher(isFulfilled(todoActions.getTodosAsync), (state, _action) => {
const action = slice.actions.updateLoading({ loading: false })
slice.caseReducers.updateLoading(state, action)
})
.addMatcher(isRejected(todoActions.getTodosAsync), (state, _action) => {
const action = slice.actions.updateLoading({ loading: false })
slice.caseReducers.updateLoading(state, action)
})
},
})
重新选择器
传递参数(可能不必要)
然而,即使状态没有改变,参数发生变化时也会重新渲染(因为缓存数为1,所以可能会频繁重新渲染)。
这很容易理解。
在这边写得很普通,但似乎不行。
自定义钩子
使用者
如果在hooks中使用在组件的DOM上设置的ref,那么只需要将其原生地用中文改写为:
如果在hooks中要使用在组件的DOM上设置的ref。
-
- 在hooks中创建ref并将其返回
- 使用方只需设置从hooks接收到的ref即可
优点
每次都能避免生成引用的利用方面
用户引用 vs 回调引用
回呼参考
如果想要在React将ref附加或分离到DOM节点时执行代码,请使用callback ref。
-
- refはcurrentが更新されても通知を受けれない
- callback refを使うと後から、クリックなどでrefに指定した要素が後から表示された場合でも変更を受け取ることができる
我来试着阅读这里的内容。
https://www.robinwieruch.de/react-ref
测试
自定义 Hooks
-
- 非同期処理は、react-testing-libraryのwaitForを使うこと
- 使わないと、処理が終わる前にexpectが走ってしまう
技巧
选择的选项值如select和option应使用const常量定义。
export const Role = {
USER: {
code: 1,
label: 'ユーザー'
},
ADMIN: {
code: 2,
label: '管理者'
},
OWNER: {
code: 3,
label: 'オーナー'
},
UNKNOWN: {
code: 0,
label: ''
}
} as const;
export type RoleKey = keyof typeof Role;
export type RoleValue = typeof Role[RoleKey];
export const findRoleByCode = (
code: number
): RoleValue =>
Object.values(Role).find(p => p.code === code) ||
Role.UNKNOWN;
在利用方面,
Object.values(Role)
可以获得一个选项,其代码为””,标签为””。
DFlex can be paraphrased in Chinese as “力自弯曲” (Lì zì .
import * as React from 'react';
import styled from '@emotion/styled';
import { css } from '@emotion/react';
type BoxProps = {
children: React.ReactNode;
direction?: string;
justify?: string;
align?: string;
} & React.InputHTMLAttributes<HTMLDivElement>;
const Wrapper = styled.div<Omit<BoxProps, 'children'>>`
display:flex;
${props => css`
flex-direction: ${props.direction ?? 'row'};
`}
${props =>
css`
align-items: ${props.align ?? 'center'};
`}
${props => css`
justify-content: ${props.justify ?? 'start'};
`}
`;
export type DFlexProps = BoxProps;
export const DFlex: React.VFC<DFlexProps> = React.memo(
({ children, ...props }) => {
return <Wrapper {...props}>{children}</Wrapper>;
}
);
DFlex.displayName = 'DFlex';
※在React组件中,由于类型错误,无法使用组件选择器,因此不需要使用memo对原子进行包装。
反应提示
更改偏移量和形状对话框箭头的位置。
<StyledTooltip
tooltipId={'testId'}
html={true}
width={`${TOOLTIP_WIDTH}px`}
offset={{ right: TOOLTIP_WIDTH / 2 - TOOLTIP_ARROW_WIDTH }}
/>
const StyledTooltip = styled(Tooltip)`
&:after {
/* 三角吹き出しの位置調整 */
left: ${TOOLTIP_ARROW_WIDTH}px !important;
}
`;