React+Apollo Link State下点击按钮切换表单渲染的优化方案咨询
优化React+Apollo Link State的表单切换逻辑
Great question! Let's clean up this redundant code and make it follow React best practices while keeping your Apollo Link State approach intact.
问题分析
你的现有实现已经能正常工作,但存在几个明显的可优化点:
- 侧边栏每个按钮都要单独定义
showXxx函数,冗余且新增表单时重复工作量大 GraphContent里的if-else判断会随着表单数量增加变得臃肿难维护GraphArea和GraphContent重复查询getEditGraph,造成不必要的缓存请求
优化方案
我们通过统一逻辑、组件映射、查询提升三个步骤重构代码,让它更简洁、可扩展。
1. 统一侧边栏按钮的点击逻辑
不需要为每个按钮单独写点击函数,让所有按钮共享一个通用的模式切换回调,只传递各自对应的mode值即可。
重构通用按钮组件
先封装一个通用的侧边栏按钮组件,复用所有按钮的结构和点击逻辑:
const SidebarButton = ({ icon, label, mode, onModeChange }) => ( <li role="presentation" className="list-inline"> <a href="" className="nav-link" onClick={(e) => { e.preventDefault(); onModeChange(mode); }}> <i className={`fa fa-${icon}`} aria-hidden="true"/> {label} </a> </li> ); // 为每个功能按钮创建复用实例 const AddNodes = (props) => <SidebarButton icon="plus" label="Add" mode="addNode" {...props} />; const LinkNodes = (props) => <SidebarButton icon="link" label="Link" mode="addLink" {...props} />; const SequenceNodes = (props) => <SidebarButton icon="list" label="Sequence" mode="sequenceNode" {...props} />; const EditNodes = (props) => <SidebarButton icon="pencil" label="Edit" mode="editNode" {...props} />; const DeleteNodes = (props) => <SidebarButton icon="trash" label="Delete" mode="deleteNode" {...props} />;
简化GraphSidebar组件
现在侧边栏只需要接收通用的模式切换回调即可:
const GraphSidebar = ({ onModeChange }) => ( <div className="avatars"> <ul> <AddNodes onModeChange={onModeChange} /> <SequenceNodes onModeChange={onModeChange} /> <EditNodes onModeChange={onModeChange} /> <LinkNodes onModeChange={onModeChange} /> <DeleteNodes onModeChange={onModeChange} /> </ul> </div> );
精简GraphArea的逻辑
用一个通用函数替代原来多个重复的showXxx函数:
class GraphArea extends Component { handleModeChange = (mode) => { this.props.updateEditGraph({ variables: { index: 'mode', value: mode } }); }; render() { const { editGraph } = this.props; return ( <div className="item"> <GraphSidebar onModeChange={this.handleModeChange} /> {/* 把editGraph直接传给子组件,避免重复查询 */} <GraphContent editGraph={editGraph} /> </div> ); } } export default compose( graphql(updateEditGraph, { name: 'updateEditGraph' }), graphql(getEditGraph, { props: ({ data: { editGraph } }) => ({ editGraph }) }) )(GraphArea);
2. 用组件映射表替代if-else判断
在GraphContent里创建一个mode与表单组件的映射对象,新增表单时只需要添加映射关系,无需修改判断逻辑。
import AddNodesForm from './AddNodesForm'; import LinkNodesForm from './LinkNodesForm'; import SequenceNodesForm from './SequenceNodesForm'; import EditNodesForm from './EditNodesForm'; import DeleteNodesForm from './DeleteNodesForm'; // 建立mode和对应表单组件的映射 const modeComponentMap = { addNode: AddNodesForm, addLink: LinkNodesForm, sequenceNode: SequenceNodesForm, editNode: EditNodesForm, deleteNode: DeleteNodesForm }; class GraphContent extends Component { render() { const { editGraph } = this.props; // 根据当前mode获取要渲染的组件 const ActiveForm = modeComponentMap[editGraph.mode]; return ( <div className="content"> {/* 如果存在对应组件则渲染,否则不显示 */} {ActiveForm && <ActiveForm />} <Graph /> </div> ); } } // 不再需要重复查询getEditGraph,直接从props接收数据 export default GraphContent;
额外优化:重置模式(可选)
如果需要在点击空白区域或切换功能时重置表单显示,可以在GraphContent中添加点击事件处理:
class GraphContent extends Component { handleContentClick = (e) => { // 点击Graph区域时重置mode if (e.target.closest('.graph-container')) { this.props.updateEditGraph({ variables: { index: 'mode', value: null } }); } }; render() { // ... 原有渲染逻辑 return ( <div className="content" onClick={this.handleContentClick}> {ActiveForm && <ActiveForm />} <Graph className="graph-container" /> </div> ); } } // 需要把updateEditGraph传递过来,或者用Apollo的useMutation钩子 export default compose( graphql(updateEditGraph, { name: 'updateEditGraph' }) )(GraphContent);
优化后的优势
- 可扩展性:新增表单时,只需要添加一个
SidebarButton实例和更新modeComponentMap,无需修改其他核心逻辑 - 减少冗余:去掉了重复的点击函数和Apollo查询,代码更简洁
- 单一职责:每个组件只负责自己的功能,数据和回调通过props传递,符合React设计理念
内容的提问来源于stack exchange,提问作者Vialito
相关产品推荐
相关产品推荐

