
ãã·ã¢èªã話ãåå¿ã³ãã¥ããã£ã®ãã£ãããé»å ±ïŒ
https://t.me/react_js ïŒã§èªããšãmobxã®è°è«ãäžå®ã®èŠåæ§ã§çŸããreduxãšã®æ¯èŒã§éæ³ãè€éãããå¯å€æ§ãã«ã€ããŠã®è°è«ãããã mobxãšã¯äœããã©ã®mobxã解決ããã®ãã«ã€ããŠã®å€§ããªèª€è§£ã ãããŠããã¹ãŠã®è°è«ã1ã€ã®æçš¿ã§åéã§ããããã«ããã®èšäºããå ±åãã§æžãããšã«ããŸããã ç¬èªã®ããŒãžã§ã³ã®mobxãå®è£
ããããšã«ãããmobxãå
éšããã©ã®ããã«æ©èœããããåæããreduxã®æ©èœãšæ¯èŒããŸãã
ãŸããmobxã¯ãç¶æ
管çã©ã€ãã©ãªãšããŠä»ã®ã©ã€ãã©ãªãšæ¯èŒãã@observable
ãã³ã¬ãŒã¿@observable
ããŒã¯ãããããããã£ãå€æŽ@observable
åŸãreactã®ã³ã³ããŒãã³ããåŒã³åºãããšãé€ããŠãç¶æ
ãæäœããããã®å©äŸ¿æ§ãã»ãšãã©æäŸããŸããã ãã¹ãŠã®@observable
ããã³@observer
ãã³ã¬ãŒã¿ãŒãåé€ããåäœäžã®ã¢ããªã±ãŒã·ã§ã³ãååŸããããšã§ã @observable
ãç°¡åã«ç Žæ£ã§ããŸããã³ã³ããŒãã³ãã«è¡šç€ºãããç¶æ
ããŒã¿ãå€æŽãããã¹ãŠã®ã€ãã³ããã³ãã©ãŒã®æåŸã«update()
ã1è¡è¿œå ããã ãã§ãã
onCommentChange(e){ const {comment} = this.props; comment.text = e.target.value; update(); // }
ãããŠupdateïŒïŒé¢æ°ã¯åã«reactã¢ããªã±ãŒã·ã§ã³ã®ãåã¬ã³ãã©ãŒããåŒã³åºããreactã®ä»®æ³éåœã®ãããã§ãå®éã®æèã§ã¯diffã®å€æŽã®ã¿ãé©çšãããŸã
function update(){ ReactDOM.render(<App>, document.getElementById('root'); }
mobxã¯ãã³ãã©ãŒå
ã®update()
1è¡ãä¿åããã®ã§ãã¹ããŒããããŒãžã£ãŒå
šäœã§ãããšèšãã®ã¯ãããã¶ãå€ãããŸãã
察ç
§çã«ãreduxã§ã¯ãç¶æ
ãé©åã«æŽæ°ããã«å€æŽãªããžã§ã¯ãïŒã¢ã¯ã·ã§ã³ïŒãããã£ã¹ããããããå®å
šã«ç°ãªãå Žæã§åŠçããå ŽåïŒããããçŽç²ãªã¬ãã¥ãŒãµãŒé¢æ°ïŒã§ãã€ãã³ããœãŒã·ã³ã°ãã¿ãŒã³ãéããŠç¶æ
ã§äœæ¥ãæŽçã§ããŸããã€ãã³ããã¹ã«ãããã«ãŠã§ã¢ãã€ãã©ã€ã³ã§ãããã®ã¢ã¯ã·ã§ã³ãã€ã³ã¿ãŒã»ããããã¿ã€ã ãã©ãã«æ©èœãä»ããŠãããã°ã¢ããªã±ãŒã·ã§ã³ãç°¡çŽ åããããšã«ãããéåæã®äŸ¿å©ãªäœæ¥ãè¿œå ã§ããŸãã
ã€ãŸããmobxã¯ç¶æ
ã®åŠçãåçŽåããã©ã€ãã©ãªã§ã¯ãããŸããããã®äž»ãªã¿ã¹ã¯ã¯äœã§ããïŒ ãã®äž»ãªã¿ã¹ã¯ã¯ãã³ã³ããŒãã³ãã®ãã€ã³ãããšã®æŽæ°ã§ããã€ãŸããå€æŽãããããŒã¿ã«äŸåããã³ã³ããŒãã³ãã®ã¿ãæŽæ°ããŸãã
äžèšã®äŸã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ããŒã¿ãå€æŽãããReactDOM.render(<App>, document.getElementById('root'))
ã«ã update()
é¢æ°ã§ReactDOM.render(<App>, document.getElementById('root'))
ãåŒã³åºããŠãã¢ããªã±ãŒã·ã§ã³å
šäœã®ãåã¬ã³ãã©ãŒãïŒä»®æ³æèæ¯èŒïŒãå®è¡ããŸããããã¯ããã©ãŒãã³ã¹ã«åœ±é¿ãã倧èŠæš¡ãªã¢ããªã±ãŒã·ã§ã³ã§ã¯ã€ã³ã¿ãŒãã§ãŒã¹ã®é床ãäœäžããããšã¯é¿ããããŸããã
çŸå®ã®éåœã¯é
ããšããã¹ããŒã¬ã³ãšãã¡ã¢ãªå
ã®ãªããžã§ã¯ãã®ããªãŒã®ã¿ãæ¯èŒããããä»®æ³éåœã§åå¿ããä»®æ³éåœãçã¿åºãããšããäºå®ã«ãããããããå®éã®éåœã§ã¯å€æŽãããéšåã®ã¿ãæŽæ°ããŸãããå®éã«ã¯ã¢ããªã±ãŒã·ã§ã³ã®ããŒã¿ãæŽæ°ããããã³ã«ãããåŒã³åºãããšã¯ã§ããŸããé
ããããã¢ããªã±ãŒã·ã§ã³å
šäœã®ä»®æ³ã®éåœãæ¯èŒããŸãã
ãããŠãåé¡ã®è§£æ±ºçã¯ä»®æ³éåœã«äŸåãããã³ã³ããŒãã³ããæåã§æŽæ°ããåºåããããŒã¿ãå€æŽãããã³ã³ããŒãã³ãã®ã¿ã®
this.forceUpdate()
åŒã³åºããŸãã
ãããŠããã®åé¡ã¯ããŸãã«mobxã©ã€ãã©ãªãšreduxã©ã€ãã©ãªã®äžéšã解決ãããã®ã§ãã
ããããããã2ã€ã®ã©ã€ãã©ãªãèæ
®ããã«ãã³ã³ããŒãã³ããåå¥ã«æŽæ°ããåé¡ã解決ããŠã¿ãŸãããã
ããã§ã¯ã2ã€ã®ã¢ãããŒããèãåºãããšãã§ããŸããã©ã¡ãããå·ãšã®é£æºæ¹æ³ã«å¶éã課ããŸãã
æåã®ã¢ãããŒãã¯äžå€æ§ãšãã€ããªæ€çŽ¢ã䜿çšããããšã§ã-åç¶æ
ã®æŽæ°ããã¹ãŠã®èŠªãªããžã§ã¯ãã«å¯ŸããŠå€æŽãããæ°ããããŒã¿ãªããžã§ã¯ããè¿ãå ŽåïŒç¶æ
ã«éå±€æ§é ãããå ŽåïŒããªã³ã¯ãåãšæ°ãããšæ¯èŒããããšã§ã³ã³ããŒãã³ãã®ã»ãŒãã€ã³ãããšã®æŽæ°ãéæã§ããŸãããŒã¿ãå€æŽãããŠããªãã³ã³ããŒãã³ãïŒnewSubtree === oldSubtreeïŒã®ãã¹ãŠã®ãµãããªãŒãã¹ããŒãã¹ããããããã®çµæãå¿
èŠãªcomã®ã¬ã³ãã©ãŒãåŒã³åºããŠã¢ããªã±ãŒã·ã§ã³ãæŽæ°ããŸãã æ§æèŠçŽ ã®æ°ã§ãã - onenta nåã®ããŒã¿ã®ã¿OïŒãã°ïŒNïŒïŒã®æåãšæ¯èŒã
ãããã£ãŠãããšãã°ã
ChangeDetectionStrategy.OnPush
èšå®ãããšè§åºŠã¯æ©èœããŸãã ããããäžããäžã«äžããšãã決å®ã«ã¯ãããã€ãã®æ¬ ç¹ããããŸãã ãŸããOïŒlogïŒnïŒïŒã®å¹çã«ãããããããããã³ã³ããŒãã³ããä»ã®ã³ã³ããŒãã³ãã®ãªã¹ãã衚瀺ããå Žåãã³ã³ããŒãã³ãã®é
åå
šäœã調ã¹ãŠå°éå
·ãæ¯èŒãããªã¹ãã®åã³ã³ããŒãã³ããå¥ã®ãªã¹ããã¬ã³ããªã³ã°ããå Žåããã®åŸãæ¯èŒã®æ°ãããã«å¢å ãââãŸãã 第äºã«-ã³ã³ããŒãã³ãã¯ç¬èªã®ããããã®ã¿ã«äŸåããå¿
èŠããããããããã¯å€ãã®å Žåãäžéã³ã³ããŒãã³ããä»ããŠã¹ããŒããå¿
èŠããããŸãã
reduxã©ã€ãã©ãªãŒãäžå€ã®ã¢ãããŒãã䜿çšããŸããããããã«å€æŽããã ãã§ãå°éå
·ã®ã¿ã«äŸåãããšããæ¬ ç¹ã解決ããŸãã reduxã¯ãå°éå
·ã®æ¯èŒã«å ããŠãç¶æ
ã®ç°ãªãéšåãžã®äŸåã瀺ã
mapStateToProps()
é¢æ°ïŒ
connect
ãã³ã¬ãŒã¿ãŒïŒã«ãã£ãŠè¿ãããè¿œå ããŒã¿ãæ¯èŒãããããã¯è¿œå ã®å°éå
·ã«ãªããŸãã ãã ãããã®ãããæ¥ç¶ã®ãã¹ãŠã®ã³ã³ããŒãã³ãããã§ãã¯ããå¿
èŠããããŸãã ãã ããããã§ãã¢ããªã±ãŒã·ã§ã³å
šäœã®æŽæ°ïŒ
ReactDOM.render(<App>, rootEl);
ïŒãããé«éã§ãã
ããããå
ç«çã¢ãããŒãã«ã¯ãåœå®¶ãšã®ååã«å¶éã課ãããã€ãã®é倧ãªæ¬ ç¹ããããŸãã
æåã®æ¬ ç¹ã¯ãã¢ããªã±ãŒã·ã§ã³å
ã®ããŒã¿ãªããžã§ã¯ãã®ããããã£ãååŸããŠæŽæ°ããããšãã§ããªãããšã§ãã ç¶æ
å
šäœã®æ°ããäžå€ãªããžã§ã¯ããæ¯åè¿ãå¿
èŠããããããæ°ãããªããžã§ã¯ããè¿ãããã¹ãŠã®èŠªãªããžã§ã¯ããšé
åãåäœæããå¿
èŠããããŸãã ããšãã°ãç¶æ
ãªããžã§ã¯ãã«ãããžã§ã¯ãã®é
åãæ ŒçŽãããŠããå Žåãåãããžã§ã¯ãã«ã¯ã¿ã¹ã¯ã®é
åãæ ŒçŽãããåã¿ã¹ã¯ã«ã¯ã³ã¡ã³ãã®é
åãæ ŒçŽãããŸãã
let AppState = { projects: [ {..}, {...}, {name: 'project3', tasts: [ {...}, {...}, {name: 'task3', comments: [ {...}, {...}, {text: 'comment3' } ]} ]} ] }
ã³ã¡ã³ããªããžã§ã¯ãã®ããã¹ããæŽæ°ããããã«ã comment.text = 'new text'
å®è¡ããããšã¯ã§ããŸãã-æåã«ã³ã¡ã³ããªããžã§ã¯ããåäœæããå¿
èŠããããŸãïŒ comment = {...comment, text: 'updated text'}
ïŒã次ã«ããå¿
èŠããããŸãã¿ã¹ã¯ãªããžã§ã¯ããåäœæããããããä»ã®ã³ã¡ã³ããžã®ãªã³ã¯ãã³ããŒãïŒ task = {...task, tasks: [...task.comments]}
ïŒããããžã§ã¯ããªããžã§ã¯ããåäœæããããã«ä»ã®ã¿ã¹ã¯ãžã®ãªã³ã¯ãã³ããŒããŸãïŒ project = {...project, tasks: [...project.tasks]}
ïŒãããŠæåŸã«æ¢ã«ç¶æ
ãªããžã§ã¯ããåäœæããä»ã®ãããžã§ã¯ããžã®ãªã³ã¯ãã³ããŒããŸãïŒ AppStat = {...AppState, projects: [...AppState.projects]}
ïŒ ã
2çªç®ã®æ¬ ç¹ã¯ãããç¶æ
ã§çžäºã«åç
§ãããªããžã§ã¯ããä¿åã§ããªãããšã§ãã ã³ã³ããŒãã³ããã³ãã©ãŒã®ã©ããã§ã¿ã¹ã¯ãé
眮ãããŠãããããžã§ã¯ããååŸããå¿
èŠãããå Žåããªããžã§ã¯ããäœæãããšãã«èŠªãããžã§ã¯ãtask.project = project
ãžã®ãªã³ã¯ãåçŽã«å²ãåœãŠãããšã¯ã§ããŸãããäžå€ã®ã¢ãããŒãã®å¿
èŠæ§ã¯ã¿ã¹ã¯ã ãã§ãªãããããžã§ã¯ãã®ãã®ä»ã®ãã¹ãŠã®ã¿ã¹ã¯ãæŽæ°ããå¿
èŠããããšããäºå®ã«ã€ãªãããŸã-ãããžã§ã¯ããªããžã§ã¯ããžã®ãªã³ã¯ãå€æŽãããŸãããã€ãŸããæ°ãããªã³ã¯ãå²ãåœãŠãããšã§ãã¹ãŠã®ã¿ã¹ã¯ãæŽæ°ããå¿
èŠããããŸãã ãªããžã§ã¯ããã¿ã¹ã¯ãã³ã¡ã³ããä¿åãããŠããå Žåãšãæã
ã¯ã圌ããåé¡ã®ãªããžã§ã¯ããžã®åç
§ãæ ŒçŽããããããã³ã¡ã³ãã®ãã¹ãŠãåäœæããããã«å¿
èŠãªããšæã
ã¯ã«æ¥ãã®ã§ãååž°çã«å
šäœã®ç¶æ
ãåäœæãããããã²ã©ãé
ããªããŸãã
ãã®çµæãç®çã®ãªããžã§ã¯ãã転éãããã³ã«é«æ¬¡ã³ã³ããŒãã³ãã®å°éå
·ãå€æŽãããããªããžã§ã¯ããåç
§ãã代ããã«task.project = '12345';
ä¿åããtask.project = '12345';
ãããŠãèå¥åã«ãã£ãŠãããžã§ã¯ãã®ããã·ã¥ãä¿åããã³ç¶æããå ŽæProjectHash['12345'] = project;
äžå€æ§ã䌎ããœãªã¥ãŒã·ã§ã³ã«ã¯å€ãã®æ¬ ç¹ããããããç¹ããŒã¹ã®ã³ã³ããŒãã³ãæŽæ°ã®åé¡ãå¥ã®æ¹æ³ã§è§£æ±ºã§ãããã©ãããèããŠã¿ãŸãããã ã¢ããªã±ãŒã·ã§ã³ã®ããŒã¿ãå€æŽããå Žåããã®ããŒã¿ã«äŸåããã³ã³ããŒãã³ãã®ã¿ãåã¬ã³ããªã³ã°ããå¿
èŠããããŸãã äŸåãšã¯ã©ãããæå³ã§ããïŒ ããšãã°ãã³ã¡ã³ãããã¹ãã衚瀺ããç°¡åãªã³ã¡ã³ãã³ã³ããŒãã³ãããããŸã
class Comment extends React.Component { render(){ const {comment} = this.props; return <div>{comment.text}</div> } }
ãã®ã³ã³ããŒãã³ãã¯comment.text
äŸåããŠããã comment.text
ãå€æŽããããã³ã«æŽæ°ããå¿
èŠããããŸãã ãã ããã³ã³ããŒãã³ãã«<div>{comment.parent.text}</div>
ã衚瀺ãããå Žåã§ãã .text
ã ãã§ãªã.parent
ã.parent
ãã³ã«ã³ã³ããŒãã³ããæŽæ°ããå¿
èŠããã.parent
ã å
ç«ã¢ãããŒããé©çšããã«ãã®åé¡ã解決ã§ããŸãããjavascriptã®getterããã³setterã®æ©èœã䜿çšããŸããããã¯ããã€ã³ãæŽæ°uiã®åé¡ã解決ããããã®2çªç®ã®ã¢ãããŒãã§ãã
ã²ãã¿ãŒãšã»ãã¿ãŒã¯ãããããã£ãæŽæ°ãããããããã£å€ãååŸããããããã³ãã©ãŒãé
眮ãããããªãå€ãjavascriptæ©èœã§ãã
Object.defineProperty(comment, 'text', { get(){ console.log('>text getter'); return this._text; }, set(val){ console.log('>text setter'); this._text = val; } }) comment.text; // >text getter comment.text = 'new text' // >text setter
ãã®ãããæ°ããå€ãå²ãåœãŠããããã³ã«å®è¡ãããé¢æ°ãã»ãã¿ãŒã«é
眮ãããã®ããããã£ã«äŸåããã³ã³ããŒãã³ãã®ãªã¹ãã®ã¬ã³ãã©ãŒãåŒã³åºããŸãã ã©ã®ã³ã³ããŒãã³ããã©ã®ããããã£ã«äŸåããŠãããã調ã¹ãã«ã¯ãã³ã³ããŒãã³ãã®render()
é¢æ°ã®éå§æã«çŸåšã®ã³ã³ããŒãã³ããã°ããŒãã«å€æ°ã«å²ãåœãŠãå¿
èŠããããŸãããªããžã§ã¯ãã®ããããã£ã®getterãåŒã³åºããšãã¯ãã°ããŒãã«å€æ°ã«ãããã®ããããã£ã®äŸåé¢ä¿ãªã¹ãã«çŸåšã®ã³ã³ããŒãã³ããè¿œå ããå¿
èŠããããŸã ãŸããã³ã³ããŒãã³ãã¯ããªãŒç¶ã«ãã¬ã³ããªã³ã°ãã§ãããããåã®ã³ã³ããŒãã³ãããã®ã°ããŒãã«å€æ°ã«æ»ãããšãå¿ããªãã§ãã ããã
let CurrentComponent; class Comment extends React.Component { render(){ const prevComponent = CurrentComponent; CurrentComponent = this; const {comment} = this.props; var result = <div>{comment.text}</div> CurrentComponent = prevComponent; return result } } comment._components = []; Object.defineProperty(comment, 'text', { get(){ this._components.push(CurrentComponent); return this._text }, set(val){ this._text = val; this._components.forEach(component => component.setState({})) } })
ç§ã¯ããªããã¢ã€ãã¢ãåŸããšæããŸãã ãã®ã¢ãããŒãã§ã¯ãåããããã£ã¯ãã®äŸåã³ã³ããŒãã³ãã®é
åãæ ŒçŽããããããã£ãå€æŽããããšããããæŽæ°ãããŸãã
ããã§ãäŸåã³ã³ããŒãã³ãã®é
åã®ã¹ãã¬ãŒãžãããŒã¿ãšæ··åãããã³ãŒããç°¡çŽ åããããã«ããã®ãããªããããã£ã®ããžãã¯ãCellã¯ã©ã¹ã«åãåºããŸããããã¯ãé¡æšãããããããã«ãExcelã®ã»ã«ã®åçã«éåžžã«äŒŒãŠããŸã-ä»ã®ã»ã«ãçŸåšã®åŒã«äŸåããŠããå Žåã»ã«ãå€ãå€æŽãããšããã¹ãŠã®äŸåã»ã«ãæŽæ°ãããŸãã
let CurrentObserver = null; class Cell { constructor(val){ this.value = val; this.reactions = new Set(); // es6 } get(){ if(CurrentObserver){ this.reactions.add(CurrentObserver); } return this.value; } set(val){ this.value = val; for(const reaction of this.reactions){ reaction.run(); } } unsibscribe(reaction){ this.reactions.delete(reaction); } }
ãã ããåŒãæã€ã»ã«ã®åœ¹å²ã¯ã Cell
ã¯ã©ã¹ããç¶æ¿ããComputedCell
ã¯ã©ã¹ã«ãã£ãŠæããããŸãïŒä»ã®ã»ã«ã¯ãã®ã»ã«ã«äŸåããå¯èœæ§ãããããïŒã ComputedCell
ã¯ã©ã¹ã¯ãã³ã³ã¹ãã©ã¯ã¿ãŒã§åèšç®ã®ããã®é¢æ°ïŒåŒïŒãšãå¯äœçšïŒ .forceUpdate()
ã³ã³ããŒãã³ãã®åŒã³åºããªã©ïŒãå®è¡ããããã®ãªãã·ã§ã³ã®é¢æ°ã.forceUpdate()
ãŸã
class ComputedCell extends Cell { constructor(computedFn, reactionFn, ){ super(undefined); this.computedFn = computedFn; this.reactionFn = reactionFn; } run(){ const prevObserver = CurrentObserver; CurrentObserver = this; const newValue = this.computedFn(); if(newValue !== this.value){ this.value = newValue; CurrentObserver = null; this.reactionFn(); this.reactions.forEach(r=>r.run()); } CurrentObserver = prevObserver; } }
ãããŠãã²ãã¿ãŒãšã»ãã¿ãŒãæ¯åå®è¡ããªãããã«ãtypescriptãŸãã¯babelã®ãã³ã¬ãŒã¿ãŒã䜿çšããŸãã ã¯ããããã¯ã¯ã©ã¹ã䜿çšãããªãã©ã«const newComment = {text: 'comment1'}
ãªãconst comment = new Comment('comment1')
ãä»ããŠãªããžã§ã¯ããäœæããå¿
èŠæ§ã«å¶éã課ããŸãããã²ãã¿ãŒãšã»ãã¿ãŒãæåã§èšå®ãã代ããã«ãããããã£ã䟿å©ã«ããŒã¯ã§ããŸã@observable
éåžžã®ããããã£ãšããŠãã@observable
åŒãç¶ã䜿çšããæ¹æ³ã
class Comment { @observable text; constructor(text){ this.text = text; } } function observable(target, key, descriptor){ descriptor.get = function(){ if(!this.__observables) this.__observables = {}; const observable = this.__observables[key]; if (!observable) this.__observables[key] = new Observable() return observable.get(); } descriptor.set = function(val){ if (!this.__observables) this.__observables = {}; const observable = this.__observables[key]; if (!observable) this.__observables[key] = new Observable() observable.set(val); } return descriptor }
ãŸããã³ã³ããŒãã³ãå
ã®ComputedCell
ã¯ã©ã¹ãçŽæ¥æäœããªãããã«ããã®ã³ãŒãã@observer
ãã³ã¬ãŒã¿ãŒã«é
眮ããŸããããã¯ã render()
ã¡ãœãããã©ããããæåã®åŒã³åºãã§èšç®ã»ã«ãäœæãã render()
ã¡ãœãããåŒããã³é¢æ°ãšããŠæž¡ããŸãthis.forceUpdate()
ã¯this.forceUpdate()
åŒã³åºãthis.forceUpdate()
å®éã«ã¯ã componentWillUnmount()
ã¡ãœããã«ãµãã¹ã¯this.forceUpdate()
解é€ãè¿œå ãããªã¢ã¯ã·ã§ã³ã®ã³ã³ããŒãã³ãã®æ£ããã©ããã³ã°ã®ç¬éãè¿œå ããå¿
èŠããããŸãããããã§ã¯ç解ã容æã«ããããã«ãã®ãªãã·ã§ã³ãæ®ããŸãïŒ
function observer(Component) { const oldRender = Component.prototype.render; Component.prototype.render = function(){ if (!this._reaction) this._reaction = new ComputedCell(oldRender.bind(this), ()=>this.forceUpdate()); return this._reaction.get(); } }
ãšããŠäœ¿çšããŸã
@observer class Comment extends React.Component { render(){ const {comment} = this.props; return <div>{comment.text}</div> } }
ãã¢ãžã®ãªã³ã¯
ãã¹ãŠã®ã³ãŒãã¢ã»ã³ã㪠import React from 'react'; import { render } from 'react-dom'; let CurrentObserver; class Cell { constructor(val) { this.value = val; this.reactions = new Set(); } get() { if (CurrentObserver) { this.reactions.add(CurrentObserver); } return this.value; } set(val) { this.value = val; for (const reaction of this.reactions) { reaction.run(); } } unsubscribe(reaction) { this.reactions.delete(reaction); } } class ComputedCell extends Cell { constructor(computedFn, reactionFn) { super(); this.computedFn = computedFn; this.reactionFn = reactionFn; this.value = this.track(); } track(){ const prevObserver = CurrentObserver; CurrentObserver = this; const newValue = this.computedFn(); CurrentObserver = prevObserver; return newValue; } run() { const newValue = this.track(); if (newValue !== this.value) { this.value = newValue; CurrentObserver = null; this.reactionFn(); } } } function observable(target, key) { return { get() { if (!this.__observables) this.__observables = {}; let observable = this.__observables[key]; if (!observable) observable = this.__observables[key] = new Cell(); return observable.get(); }, set(val) { if (!this.__observables) this.__observables = {}; let observable = this.__observables[key]; if (!observable) observable = this.__observables[key] = new Cell(); observable.set(val); } } } function observer(Component) { const oldRender = Component.prototype.render; Component.prototype.render = function(){ if (!this._reaction) this._reaction = new ComputedCell(oldRender.bind(this), ()=>this.forceUpdate()); return this._reaction.get(); } } class Timer { @observable count; constructor(text) { this.count = 0; } } const AppState = new Timer(); @observer class App extends React.Component { onClick=()=>{ this.props.timer.count++ } render(){ console.log('render'); const {timer} = this.props; return ( <div> <div>{timer.count}</div> <button onClick={this.onClick}>click</button> </div> ) } } render(<App timer={AppState}/>, document.getElementById('root'));
ãã®äŸã§ã¯ã1ã€ã®æ¬ ç¹ããããŸããã³ã³ããŒãã³ãã®äŸåé¢ä¿ãå€æŽãããå¯èœæ§ãããå Žåã¯ã©ãã§ããããã 次ã®ã³ã³ããŒãã³ããã芧ãã ãã
class User extends React.Component { render(){ const {user} = this.props; return <div>{user.showFirstName ? user.firstName : user.lastName}</div> } }
ã³ã³ããŒãã³ãã¯user.showFirstName
ããããã£ã«äŸåããããã«å€ã«å¿ããŠã user.firstName
ãŸãã¯user.lastName
ããããã«äŸåã§ããŸããã€ãŸãã user.showFirstName == true
å Žåã user.showFirstName == true
å€æŽã«åå¿ããã¹ãã§ã¯ãããŸããã user.showFirstName
falseã«å€æŽããããããããããã£user.firstName
å Žåãåå¿ïŒããã³ã³ã³ããŒãã³ãã¬ã³ãã©ãŒãå®è¡ïŒããªãã§user.firstName
ã
ãã®ç¹ã¯ãäŸåé¢ä¿ãªã¹ãthis.dependencies = new Set()
ãã»ã«ã¯ã©ã¹ã«è¿œå ãã run()
é¢æ°ã®å°ããªããžãã¯ãè¿œå ããããšã§ç°¡åã«è§£æ±ºã§ããŸãããã®ãããåå¿ã®renderïŒïŒãåŒã³åºããåŸã以åã®äŸåé¢ä¿ã®ãªã¹ããæ°ãããã®ãšæ¯èŒããç¡é¢ä¿ãªäŸåé¢ä¿ãããµãã¹ã¯ã©ã€ãã解é€ããŸãã
class Cell { constructor(){ ... this.dependencies = new Set(); } get() { if (CurrentObserver) { this.reactions.add(CurrentObserver); CurrentObserver.dependencies.add(this); } return this.value; } } class ComputedCell { track(){ const prevObserver = CurrentObserver; CurrentObserver = this; const oldDependencies = this.dependencies;
2çªç®ã®ãã€ã³ãã¯ããªããžã§ã¯ãã®å€ãã®ããããã£ãããã«å€æŽããå Žåã§ããïŒ äŸåã³ã³ããŒãã³ãã¯åæçã«æŽæ°ãããããã2ã€ã®è¿œå ã³ã³ããŒãã³ãæŽæ°ãååŸãããŸã
comment.text = 'edited text'; // comment.editedCount+=1; //
äžèŠãªæŽæ°ãé¿ããããã«ããã®é¢æ°ã®å
é ã§ã°ããŒãã«ãã©ã°ãèšå®ã§ããŸãã @observer
ãã³ã¬ãŒã¿ãŒã¯ããã«this.forceUpdate()
åŒã³åºããããã®ãã©ã°ãåé€ãããšãã«ã®ã¿åŒã³åºããŸãã ãããŠç°¡åã«ããããã«ããã®ããžãã¯ãaction
ãã³ã¬ãŒã¿ã«é
眮ãããã©ã°ã®ä»£ããã«ãã³ã¬ãŒã¿ãä»ã®ãã³ã¬ãŒã¿å
ã§åŒã³åºãããšãã§ãããããã«ãŠã³ã¿ãå¢æžããŸãã
updatedComment = action(()=>{ comment.text = 'edited text'; comment.editedCount+=1; }) let TransactionCount = 0; let PendingComponents = new Set(); function observer(Component) { const oldRender = Component.prototype.render; Component.prototype.render = function(){ if (!this._reaction) this._reaction = new ComputedCell(oldRender.bind(this), ()=>{ TransactionCount ?PendingComponents.add(this) : this.forceUpdate() }); return this._reaction.get(); } } function action(fn){ TransactionCount++ const result = fn(); TransactionCount-- if(TransactionCount == 0){ for(const component of PendingComponents){ component.forceUpdate(); } } return result; }
ãã®çµæãéåžžã«å€ãããªãã¶ãŒããŒããã¿ãŒã³ã䜿çšãããã®ã¢ãããŒãïŒãªãã¶ãŒããã«RxJSãšæ··åããªãã§ãã ããïŒã¯ãã€ãã¥ããã£ã¢ãããŒãããããã€ã³ãæŽæ°ã³ã³ããŒãã³ãã®ã¿ã¹ã¯ã«ããé©ããŠããŸãã
æ¬ ç¹ã®ãã¡ããªãã©ã«ã§ã¯ãªãã¯ã©ã¹ãä»ããŠãªããžã§ã¯ããäœæããå¿
èŠãããããšã«æ°ä»ãããšãã§ããŸããã€ãŸãããµãŒããŒããäžéšã®ããŒã¿ã@observable
ãŠã³ã³ããŒãã³ãã«æž¡ãããšã¯ã§ããŸãããã¯ã©ã¹ãªããžã§ã¯ãã@observable
ãã³ã¬ãŒã¿ã§ã©ããããŠè¿œå ã®ããŒã¿åŠçãå®è¡ããå¿
èŠããããŸãã
æ¬ ç¹ã«ã¯ããªããžã§ã¯ãã«æ°ããããããã£ããªã³ã¶ãã©ã€ã§è¿œå ã§ããªãããšïŒjsããã©ãŒãã³ã¹ã®èŠ³ç¹ããæ¢ã«ã¢ã³ããã¿ãŒã³ãšèŠãªãããŠããŸãïŒãããŒã¿ãã²ãã¿ãŒã®èåŸã«é ãããå€ã®ä»£ããã«3ã€ã®ãããã衚瀺ããããããã¯ãã devtoolsã®ã³ãŒããããã°ã®äžäŸ¿ããå«ãŸããŸããã®ããããã£ãã¯ãªãã¯ããå€ãããã³å€æŽãã¹ãããã¹ã«ãŒããããããããã£ãååŸããããšãããšãã©ã€ãã©ãªå
ã®ã»ãã¿ãŒãŸãã¯ã²ãã¿ãŒã«æ·±ãå
¥ããŸãã
ããããå©ç¹ã¯æ¬ ç¹ãã¯ããã«è¶
ããŠããŸãã ãŸããäžå€ã®ã¢ãããŒããšã¯ç°ãªããæŽæ°ã®å¿
èŠãªã³ã³ããŒãã³ãã®ãªã¹ããããã«ããããããäœæ¥ã®é床ã¯ã³ã³ããŒãã³ãã®æ°ã«äŸåããŸãã-ã€ãŸããoïŒlogïŒnïŒïŒãŸãã¯oïŒnïŒã®ä»£ããã«oïŒ1ïŒã®è€éãããããŸã
Den Abramovãããã«éèŠãªããšã«ãnãªããžã§ã¯ãã¯
mapStateToProps
é¢æ°ã§ã¯äœæãããŸããã 次ã«ãäžéšã®ããŒã¿ãæŽæ°ããå¿
èŠãããå Žåã
comment.text = 'new text'
ãšèšè¿°ããã ãã§ã芪ã®ç¶æ
ãªããžã§ã¯ããæŽæ°ããããã«å€ãã®äœæ¥ãè¡ãå¿
èŠã¯ãããŸãããéèŠãªããšã¯ãã¬ããŒãžã³ã¬ã¯ã¿ãŒã«è² è·ãããããªãããšã§ãããªããžã§ã¯ãã®æ°žç¶çãªã¬ã¯ãªãšãŒã·ã§ã³ã æãéèŠãªããš-çžäºã«åç
§ãããªããžã§ã¯ãã䜿çšããŠç¶æ
ãã¢ãã«åã§ãããªããžã§ã¯ãã®ä»£ããã«èå¥åãä¿åããããšãªãç¶æ
ãæäœã§ãã
AppState.folders[AppState.projects[AppState.tasks[comment.taskId].projectId].folderId].name
ã ãã§
AppState.folders[AppState.projects[AppState.tasks[comment.taskId].projectId].folderId].name
ã¯ãªãã¯ãã代ããã«
ãããã«
â â "" mobx. mobx @computed
( ) mobx- react-.