概述
目录
- 前言
- 一、background
- 1.1 需求
- 1.2 example
- 二、实现
- 2.1 example
- 2.2 useToggle实现
- 2.3 设计新的dispatch
- 2.4 调用user的onChange逻辑
- 2.5 final version
- 总结
前言
最近开始学习React,跟着Kent学,有很多干货,这里分享Rect中的一个设计模式Control Props,这个设计方法跟React中的Control Component
文章中完整的示例代码可以查看 这里
一、background
1.1 需求
有时候用户希望能够控制state的改变,比如在Inpt的组件中,用户希望控制input中的value以及value改变的action,onChange;对于DOM组件来说,React已经提供了受控组件
对于自定义组件,也可以设计出受控props,只要用户需要,props的控制权应该可以交给用户
1.2 example
比如,对于自定义的Toggle组件,用户可以控制on以及onChange;如果用户不愿意控制,那么就使用props的default on以及onChange
<Toggle on={bothOn} onChange={handleToggleChange} />
<Toggle on={bothOn} onChange={handleToggleChange} />
二、实现
2.1 example
这里会借用之前的Toggle组件的例子,向外提供control props的功能
2.2 useToggle实现
useToggle主要是给组件调用的props的,比如用户新建Toggle组件,那么组件需要用的props统一从useToggle中获得
首先不妨先设计useToggle可以接受的参数,其中on以及inChange都是需要做成受控props的
function useToggle({
initialOn = false,
reducer = toggleReducer,
onChange,
on: controlledOn
} = {}) {
由于Toggle向外会提供两个方法,toggle以及reset,在user么有修改情况之下,使用dispatch方法就行了
const toggle = () => dispatch({type: actionTypes.toggle})
const reset = () => dispatch({type: actionTypes.reset, initialState})
但是如果需要提供受控props,那么如果user提供的话,就应该使用user提供的
这里不妨设想user控制props情景如下,user控制on,提供了onChange的方法
<Toggle on={bothOn} onChange={handleToggleChange} />
function handleToggleChange(state, action) {
if (action.type === actionTypes.toggle && timesClicked > 4) {
return
}
setBothOn(state.on)
setTimesClicked(c => c + 1)
}
也就是说,对于useToggle的dispatch来说需要处理两种情况
- user没有控制props,使用dispatch
- user控制props,调用user控制的方法
2.3 设计新的dispatch
根据上面的要求,不妨设计新的dispatch
function dispatchWithOnChange(action) {
if (!onIsControlled) {
dispatch(action)
}
onChange?.(reducer({...state, on}, action), action)
}
const toggle = () => dispatchWithOnChange({type: actionTypes.toggle})
const reset = () => dispatchWithOnChange({type: actionTypes.reset, initialState})
2.4 调用user的onChange逻辑
这里主要讲一下,调用user方法的逻辑
onChange?.(reducer({...state, on}, action), action)
从2.2的API可以知道,user调用的是
onChange = {()=>handleToggleChange(state, action)}
所以需要传新的state以及action,使用reduedr(state, action)可以返回新的state,由于on也是受控props,所以需要单独提取出来,如下
(reducer({...state, on}, action)
2.5 final version
全部的实现(Switch组件以及css)可以到前言中codesandbox链接中参考
import * as React from "react";
import { Switch } from "./switch";
const callAll = (...fns) => (...args) => fns.forEach((fn) => fn?.(...args));
const actionTypes = {
toggle: "toggle",
reset: "reset"
};
function toggleReducer(state, { type, initialState }) {
switch (type) {
case actionTypes.toggle: {
return { on: !state.on };
}
case actionTypes.reset: {
return initialState;
}
default: {
throw new Error(`Unsupported type: ${type}`);
}
}
}
function useToggle({
initialOn = false,
reducer = toggleReducer,
onChange,
on: controlledOn
} = {}) {
const { current: initialState } = React.useRef({ on: initialOn });
const [state, dispatch] = React.useReducer(reducer, initialState);
console.log("test on is controller ", controlledOn);
const onIsControlled = controlledOn != null;
const on = onIsControlled ? controlledOn : state.on;
function dispatchWithOnChange(action) {
if (!onIsControlled) dispatch(action);
onChange?.(reducer({ ...state, on }, action), action);
}
const toggle = () => dispatchWithOnChange({ type: actionTypes.toggle });
const reset = () =>
dispatchWithOnChange({ type: actionTypes.reset, initialState });
function getTogglerProps({ onClick, ...props } = {}) {
return {
"aria-pressed": on,
onClick: callAll(onClick, toggle),
...props
};
}
function getResetterProps({ onClick, ...props } = {}) {
return {
onClick: callAll(onClick, reset),
...props
};
}
return {
on,
reset,
toggle,
getTogglerProps,
getResetterProps
};
}
function Toggle({ on: controlledOn, onChange }) {
const { on, getTogglerProps } = useToggle({ on: controlledOn, onChange });
const props = getTogglerProps({ on });
return <Switch {...props} />;
}
function App() {
const [bothOn, setBothOn] = React.useState(false);
const [timesClicked, setTimesClicked] = React.useState(0);
function handleToggleChange(state, action) {
if (action.type === actionTypes.toggle && timesClicked > 4) {
return;
}
setBothOn(state.on);
setTimesClicked((c) => c + 1);
}
function handleResetClick() {
setBothOn(false);
setTimesClicked(0);
}
return (
<div>
<div>
<Toggle on={bothOn} onChange={handleToggleChange} />
<Toggle on={bothOn} onChange={handleToggleChange} />
</div>
{timesClicked > 4 ? (
<div data-testid="notice">
Whoa, you clicked too much!
<br />
</div>
) : (
<div data-testid="click-count">Click count: {timesClicked}</div>
)}
<button onClick={handleResetClick}>Reset</button>
<hr />
<div>
<div>Uncontrolled Toggle:</div>
<Toggle
onChange={(...args) =>
console.info("Uncontrolled Toggle onChange", ...args)
}
/>
</div>
</div>
);
}
export default App;
export { Toggle };
总结
受控props可以类比受控组件,目的就是为了让用户有更多的控制权,以方便用户更多自定义的方法,提高了组建的可复用性
最后
以上就是超帅老师为你收集整理的React中的设计模式 - 受控属性Control Props前言一、background二、实现总结的全部内容,希望文章能够帮你解决React中的设计模式 - 受控属性Control Props前言一、background二、实现总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复