è¿ç¯å¿«é䏿æåä¼æä½ å¦ä½å°TypeScriptä¸Reactç»åèµ·æ¥ä½¿ç¨ã 卿åï¼ä½ å°å¦å°ï¼
- 使ç¨TypeScriptåReactå建工ç¨
- 使ç¨TSLintè¿è¡ä»£ç æ£æ¥
- 使ç¨JeståEnzymeè¿è¡æµè¯ï¼ä»¥å
- 使ç¨Redux管çç¶æ
æä»¬ä¼ä½¿ç¨create-react-appå·¥å ·å¿«éæå»ºå·¥ç¨ç¯å¢ã
è¿éåè®¾ä½ å·²ç»å¨ä½¿ç¨Node.jsånpmã å¹¶ä¸å·²ç»äºè§£äºReactçåºç¡ç¥è¯ã
å®è£ create-react-app
æä»¬ä¹æä»¥ä½¿ç¨create-react-appæ¯å 为å®è½å¤ä¸ºReactå·¥ç¨è®¾ç½®ä¸äºææçå·¥å ·åæå¨çé»è®¤åæ°ã å®ä» ä» æ¯ä¸ä¸ªç¨æ¥æå»ºReactå·¥ç¨çå½ä»¤è¡å·¥å ·èå·²ã
npm install -g create-react-app
å建æ°å·¥ç¨
让æä»¬é¦å
å建ä¸ä¸ªå«åmy-appçæ°å·¥ç¨ï¼
create-react-app my-app --scripts-version=react-scripts-ts
react-scripts-tsæ¯ä¸ç³»åéé å¨ï¼å®å©ç¨æ åçcreate-react-appå·¥ç¨ç®¡éå¹¶æTypeScriptæ··å ¥è¿æ¥ã
æ¤æ¶çå·¥ç¨ç»æåºå¦ä¸æç¤ºï¼
my-app/
ââ .gitignore
ââ node_modules/
ââ public/
ââ src/
â ââ ...
ââ package.json
ââ tsconfig.json
ââ tslint.json
注æï¼
tsconfig.jsonå å«äºå·¥ç¨éTypeScriptç¹å®çé项ãtslint.jsonä¿åäºè¦ä½¿ç¨çä»£ç æ£æ¥å¨ç设置ï¼TSLintãpackage.jsonå å«äºä¾èµï¼è¿æä¸äºå½ä»¤çå¿«æ·æ¹å¼ï¼å¦æµè¯å½ä»¤ï¼é¢è§å½ä»¤ååå¸åºç¨çå½ä»¤ãpublicå å«äºéæèµæºå¦HTML页颿å¾çãé¤äºindex.htmlæä»¶å¤ï¼å ¶å®çæä»¶é½å¯ä»¥å é¤ãsrcå å«äºTypeScriptåCSSæºç ãindex.tsxæ¯å¼ºå¶ä½¿ç¨çå ¥å£æä»¶ã
è¿è¡å·¥ç¨
éè¿ä¸é¢çæ¹å¼å³å¯è½»æ¾å°è¿è¡è¿ä¸ªå·¥ç¨ã
npm run start
å®ä¼æ§è¡package.jsoné颿å®çstartå½ä»¤ï¼å¹¶ä¸ä¼å¯å¨ä¸ä¸ªæå¡å¨ï¼å½æä»¬ä¿åæä»¶æ¶è¿ä¼èªå¨å·æ°é¡µé¢ã
é常è¿ä¸ªæå¡å¨çå°åæ¯http://localhost:3000ï¼é¡µé¢åºç¨ä¼è¢«èªå¨å°æå¼ã
å®ä¼ä¿æçå¬ä»¥æ¹ä¾¿æä»¬å¿«éå°é¢è§æ¹å¨ã
æµè¯å·¥ç¨
æµè¯ä¹ä» ä» æ¯ä¸è¡å½ä»¤çäºå¿ï¼
npm run test
è¿ä¸ªå½ä»¤ä¼è¿è¡Jestï¼ä¸ä¸ªé常好ç¨çæµè¯å·¥å
·ï¼å®ä¼è¿è¡æææ©å±åæ¯.test.tsæ.spec.tsçæä»¶ã
å¥½æ¯æ¯npm run startå½ä»¤ï¼å½æ£æµå°ææ¹å¨çæ¶åJestä¼èªå¨å°è¿è¡ã
妿忬¢çè¯ï¼ä½ è¿å¯ä»¥åæ¶è¿è¡npm run startånpm run testï¼è¿æ ·ä½ å°±å¯ä»¥å¨é¢è§çåæ¶è¿è¡æµè¯ã
çæç产ç¯å¢çæå»ºçæ¬
å¨ä½¿ç¨npm run startè¿è¡å·¥ç¨çæ¶åï¼æä»¬å¹¶æ²¡æçæä¸ä¸ªä¼åè¿ççæ¬ã
é常æä»¬æ³ç»ç¨æ·ä¸ä¸ªè¿è¡çå°½å¯è½å¿«å¹¶å¨ä½ç§¯ä¸å°½å¯è½å°ç代ç ã
ååç¼©è¿æ ·çä¼åæ¹æ³å¯ä»¥åå°è¿ä¸ç¹ï¼ä½æ¯æ»æ¯è¦èè´¹æ´å¤çæ¶é´ã
æä»¬æè¿æ ·çæå»ºçæ¬ç§°åâç产ç¯å¢âçæ¬ï¼ä¸å¼åçæ¬ç¸å¯¹ï¼ã
è¦æ§è¡ç产ç¯å¢çæå»ºï¼å¯ä»¥è¿è¡å¦ä¸å½ä»¤ï¼
npm run build
è¿ä¼ç¸åºå°å建ä¼åè¿çJSåCSSæä»¶ï¼./build/static/jså./build/static/cssã
大夿°æ åµä¸ä½ ä¸éè¦çæç产ç¯å¢çæå»ºçæ¬ï¼ ä½å®å¯ä»¥å¸®å©ä½ è¡¡éåºç¨æç»çæ¬çä½ç§¯å¤§å°ã
å建ä¸ä¸ªç»ä»¶
ä¸é¢æä»¬å°è¦å建ä¸ä¸ªHelloç»ä»¶ã
è¿ä¸ªç»ä»¶æ¥æ¶ä»»æä¸ä¸ªæä»¬æ³å¯¹ä¹ææå¼çååï¼æä»¬æå®å«ånameï¼ï¼å¹¶ä¸æä¸ä¸ªå¯éæ°éçæå¹å·å为ç»å°¾ï¼éè¿enthusiasmLevelï¼ã
è¥æä»¬è¿æ ·å<Hello name="Daniel" enthusiasmLevel={3} />ï¼è¿ä¸ªç»ä»¶å¤§è³ä¼æ¸²ææ<div>Hello Daniel!!!</div>ã
å¦ææ²¡æå®enthusiasmLevelï¼ç»ä»¶å°é»è®¤æ¾ç¤ºä¸ä¸ªæå¹å·ã
è¥enthusiasmLevel为0æè´å¼å°æåºä¸ä¸ªé误ã
ä¸é¢æ¥åä¸ä¸Hello.tsxï¼
// src/components/Hello.tsx
import * as React from 'react';
export interface Props {
name: string;
enthusiasmLevel?: number;
}
function Hello({ name, enthusiasmLevel = 1 }: Props) {
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
</div>
);
}
export default Hello;
// helpers
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('!');
}
注ææä»¬å®ä¹äºä¸ä¸ªç±»åPropsï¼å®æå®äºæä»¬ç»ä»¶è¦ç¨å°ç屿§ã
nameæ¯å¿
éçä¸ä¸ºstringç±»åï¼åæ¶enthusiasmLevelæ¯å¯éçä¸ä¸ºnumberç±»åï¼ä½ å¯ä»¥éè¿åååé¢å ?为æå®å¯éåæ°ï¼ã
æä»¬å建äºä¸ä¸ªæ ç¶æç彿°å¼ç»ä»¶ï¼Stateless Functional Componentsï¼SFCï¼Helloã
å
·ä½æ¥è®²ï¼Helloæ¯ä¸ä¸ªå½æ°ï¼æ¥æ¶ä¸ä¸ªProps对象并æè§£å®ã
妿Propså¯¹è±¡éæ²¡æè®¾ç½®enthusiasmLevelï¼é»è®¤å¼ä¸º1ã
使ç¨å½æ°æ¯Reactä¸å®ä¹ç»ä»¶çä¸¤ç§æ¹å¼ä¹ä¸ã å¦æä½ åæ¬¢çè¯ï¼ä¹å¯ä»¥éè¿ç±»çæ¹å¼å®ä¹ï¼
class Hello extends React.Component<Props, object> {
render() {
const { name, enthusiasmLevel = 1 } = this.props;
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
</div>
);
}
}
彿们çç»ä»¶å
·ææäºç¶æçæ¶åï¼ä½¿ç¨ç±»çæ¹å¼æ¯å¾æç¨å¤çã
ä½å¨è¿ä¸ªä¾åéæä»¬ä¸éè¦èèç¶æ - äºå®ä¸ï¼å¨React.Component<Props, object>æä»¬æç¶ææå®ä¸ºäºobjectï¼å æ¤ä½¿ç¨SFCæ´ç®æ´ã
å½å¨å建å¯éç¨çéç¨UIç»ä»¶çæ¶åï¼å¨è¡¨ç°å±ä½¿ç¨ç»ä»¶å±é¨ç¶ææ¯è¾éåã
é对æä»¬åºç¨ççå½å¨æï¼æä»¬ä¼å®¡è§åºç¨æ¯å¦ä½éè¿Reduxè½»æ¾å°ç®¡çæ®éç¶æçã
ç°å¨æä»¬å·²ç»å好äºç»ä»¶ï¼è®©æä»¬ä»ç»ççindex.tsxï¼æ<App />æ¿æ¢æ<Hello ... />ã
é¦å æä»¬å¨æä»¶å¤´é¨å¯¼å ¥å®ï¼
import Hello from './components/Hello.tsx';
ç¶åä¿®æ¹renderè°ç¨ï¼
ReactDOM.render(
<Hello name="TypeScript" enthusiasmLevel={10} />,
document.getElementById('root') as HTMLElement
);
ç±»åæè¨
è¿éè¿æä¸ç¹è¦æåºï¼å°±æ¯æåä¸è¡document.getElementById('root') as HTMLElementã
è¿ä¸ªè¯æ³å«åç±»åæè¨ï¼ææ¶ä¹å«å转æ¢ã
å½ä½ æ¯ç±»åæ£æ¥å¨æ´æ¸
æ¥ä¸ä¸ªè¡¨è¾¾å¼çç±»åçæ¶åï¼ä½ å¯ä»¥éè¿è¿ç§æ¹å¼éç¥TypeScriptã
è¿éï¼æä»¬ä¹æä»¥è¿ä¹åæ¯å 为getElementByIdçè¿åå¼ç±»åæ¯HTMLElement | nullã
ç®åå°è¯´ï¼getElementByIdè¿ånullæ¯å½æ æ³æ¾å¯¹å¯¹åºidå
ç´ çæ¶åã
æä»¬å设getElementByIdæ»æ¯æåçï¼å æ¤æä»¬è¦ä½¿ç¨asè¯æ³åè¯TypeScriptè¿ç¹ã
TypeScriptè¿æä¸ç§æå¹å·ï¼!ï¼ç»å°¾çè¯æ³ï¼å®ä¼ä»åé¢ç表达å¼éç§»é¤nullåundefinedã
æä»¥æä»¬ä¹å¯ä»¥åædocument.getElementById('root')!ï¼ä½å¨è¿éæä»¬æ³åçæ´æ¸
æ¥äºã
:sunglasses:æ·»å æ ·å¼
éè¿æä»¬ç设置为ä¸ä¸ªç»ä»¶æ·»å æ ·å¼å¾å®¹æã
è¥è¦è®¾ç½®Helloç»ä»¶çæ ·å¼ï¼æä»¬å¯ä»¥åå»ºè¿æ ·ä¸ä¸ªCSSæä»¶src/components/Hello.cssã
.hello {
text-align: center;
margin: 20px;
font-size: 48px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}
.hello button {
margin-left: 25px;
margin-right: 25px;
font-size: 40px;
min-width: 50px;
}
create-react-appå
å«çå·¥å
·ï¼Webpackåä¸äºå è½½å¨ï¼å
许æä»¬å¯¼å
¥æ ·å¼è¡¨æä»¶ã
彿们æå»ºåºç¨çæ¶åï¼ææå¯¼å
¥ç.cssæä»¶ä¼è¢«æ¼æ¥æä¸ä¸ªè¾åºæä»¶ã
å æ¤å¨src/components/Hello.tsxï¼æä»¬éè¦æ·»å å¦ä¸å¯¼å
¥è¯å¥ã
import './Hello.css';
使ç¨Jestç¼åæµè¯
æä»¬å¯¹Helloç»ä»¶æä¸äºå设ã
让æä»¬å¨æ¤éç³ä¸ä¸ï¼
- å½è¿æ ·å
<Hello name="Daniel" enthusiasmLevel={3} />æ¶ï¼ç»ä»¶åºè¢«æ¸²ææ<div>Hello Daniel!!!</div>ã- è¥æªæå®
enthusiasmLevelï¼ç»ä»¶åºé»è®¤æ¾ç¤ºä¸ä¸ªæå¹å·ã- è¥
enthusiasmLevel为0æè´å¼ï¼å®åºæåºä¸ä¸ªé误ã
æä»¬å°é对è¿äºéæ±ä¸ºç»ä»¶åä¸äºæ³¨éã
ä½é¦å ï¼æä»¬è¦å®è£ Enzymeã Enzymeæ¯Reactçæç³»ç»éä¸ä¸ªéç¨å·¥å ·ï¼å®æ¹ä¾¿äºé对ç»ä»¶çè¡ä¸ºç¼åæµè¯ã é»è®¤å°ï¼æä»¬çåºç¨å å«äºä¸ä¸ªå«åjsdomçåºï¼å®å 许æä»¬æ¨¡æDOM以åå¨éæµè§å¨çç¯å¢ä¸æµè¯è¿è¡æ¶çè¡ä¸ºã Enzyme䏿¤ç±»ä¼¼ï¼ä½æ¯æ¯åºäºjsdomçï¼å¹¶ä¸æ¹ä¾¿æä»¬æ¥è¯¢ç»ä»¶ã
让æä»¬æå®å®è£ 为å¼åä¾èµé¡¹ã
npm install -D enzyme @types/enzyme react-addons-test-utils
注ææä»¬åæ¶å®è£
äºenzymeå@types/enzymeã
enzymeå
æçæ¯å
å«äºå®é
è¿è¡çJavaScript代ç å
ï¼è@types/enzymeåå
å«äºå£°ææä»¶ï¼.d.tsæä»¶ï¼çå
ï¼ä»¥ä¾¿TypeScriptè½å¤äºè§£è¯¥å¦ä½ä½¿ç¨Enzymeã
ä½ å¯ä»¥å¨è¿éäºè§£æ´å¤å
³äº@typeså
çä¿¡æ¯ã
æä»¬è¿éè¦å®è£
react-addons-test-utilsã
宿¯ä½¿ç¨enzymeæéè¦å®è£
çå
ã
ç°å¨æä»¬å·²ç»è®¾ç½®å¥½äºEnzymeï¼ä¸é¢å¼å§ç¼åæµè¯ï¼
å
å建ä¸ä¸ªæä»¶src/components/Hello.test.tsxï¼ä¸å
åçHello.tsxæä»¶æ¾å¨ä¸èµ·ã
// src/components/Hello.test.tsx
import * as React from 'react';
import * as enzyme from 'enzyme';
import Hello from './Hello';
it('renders the correct text when no enthusiasm level is given', () => {
const hello = enzyme.shallow(<Hello name='Daniel' />);
expect(hello.find(".greeting").text()).toEqual('Hello Daniel!')
});
it('renders the correct text with an explicit enthusiasm of 1', () => {
const hello = enzyme.shallow(<Hello name='Daniel' enthusiasmLevel={1}/>);
expect(hello.find(".greeting").text()).toEqual('Hello Daniel!')
});
it('renders the correct text with an explicit enthusiasm level of 5', () => {
const hello = enzyme.shallow(<Hello name='Daniel' enthusiasmLevel={5} />);
expect(hello.find(".greeting").text()).toEqual('Hello Daniel!!!!!');
});
it('throws when the enthusiasm level is 0', () => {
expect(() => {
enzyme.shallow(<Hello name='Daniel' enthusiasmLevel={0} />);
}).toThrow();
});
it('throws when the enthusiasm level is negative', () => {
expect(() => {
enzyme.shallow(<Hello name='Daniel' enthusiasmLevel={-1} />);
}).toThrow();
});
è¿äºæµè¯é½åååºç¡ï¼ä½ä½ å¯ä»¥ä»ä¸å¾å°å¯åã
æ·»å state管ç
å°æ¤ä¸ºæ¢ï¼å¦æä½ 使ç¨Reactçç®çæ¯åªè·å䏿¬¡æ°æ®å¹¶æ¾ç¤ºï¼é£ä¹ä½ å·²ç»å®æäºã 使¯å¦æä½ æ³å¼åä¸ä¸ªå¯ä»¥äº¤äºçåºç¨ï¼é£ä¹ä½ éè¦æ·»å state管çã
stateç®¡çæ¦è¿°
Reactæ¬èº«å°±æ¯ä¸ä¸ªéåäºå建å¯ç»ååè§å¾çåºã 使¯ï¼React并没æä»»ä½å¨åºç¨é´åæ¥æ°æ®çåè½ã å°±Reactç»ä»¶èè¨ï¼æ°æ®æ¯éè¿æ¯ä¸ªå ç´ ä¸æå®çpropsååå ç´ ä¼ éã
å 为Reactæ¬èº«å¹¶æ²¡ææä¾å ç½®çstate管çåè½ï¼React社åºéæ©äºReduxåMobXåºã
Reduxä¾é ä¸ä¸ªç»ä¸ä¸ä¸å¯åçæ°æ®å卿¥åæ¥æ°æ®ï¼å¹¶ä¸æ´æ°é£éçæ°æ®æ¶ä¼è§¦ååºç¨çæ´æ°æ¸²æã stateçæ´æ°æ¯ä»¥ä¸ç§ä¸å¯åçæ¹å¼è¿è¡ï¼å®ä¼åå¸ä¸æ¡æç¡®çactionæ¶æ¯ï¼è¿ä¸ªæ¶æ¯å¿ 须被reducer彿°å¤çã ç±äºä½¿ç¨äºè¿æ ·æç¡®çæ¹å¼ï¼å¾å®¹æå¼æ¸ æ¥ä¸ä¸ªactionæ¯å¦ä½å½±åç¨åºçstateã
MobXåå©äºå½æ°å¼ååºå模å¼ï¼state被å è£ å¨äºå¯è§å¯å¯¹è±¡éï¼å¹¶éè¿propsä¼ éã éè¿å°stateæ 记为å¯è§å¯çï¼å³å¯å¨ææè§å¯è ä¹é´ä¿æstateç忥æ§ã å¦ä¸ä¸ªå¥½å¤æ¯ï¼è¿ä¸ªåºå·²ç»ä½¿ç¨TypeScriptå®ç°äºã
è¿ä¸¤è åæä¼ç¼ºç¹ã ä½Redux使ç¨å¾æ´å¹¿æ³ï¼å æ¤å¨è¿ç¯æç¨éï¼æä»¬ä¸»è¦çå¦ä½ä½¿ç¨Reduxï¼ ä½æ¯ä¹é¼å±å¤§å®¶ä¸¤è é½å»äºè§£ä¸ä¸ã
åé¢çå°èå¦ä¹ æ²çº¿æ¯è¾é¡ã å æ¤å¼ºç建议大家å å»çæä¸ä¸Reduxã
设置actions
åªæå½åºç¨éçstate伿¹åçæ¶åï¼æä»¬æéè¦å»æ·»å Reduxã æä»¬éè¦ä¸ä¸ªactionçæ¥æºï¼å®å°è§¦åæ¹åã å®å¯ä»¥æ¯ä¸ä¸ªå®æ¶å¨æè UIä¸çä¸ä¸ªæé®ã
为æ¤ï¼æä»¬å°å¢å 两个æé®æ¥æ§å¶Helloç»ä»¶çæå¹çº§å«ã
å®è£ Redux
å®è£
reduxåreact-redux以åå®ä»¬çç±»åæä»¶å为ä¾èµã
npm install -S redux react-redux @types/react-redux
è¿éæä»¬ä¸éè¦å®è£
@types/reduxï¼å 为Reduxå·²ç»èªå¸¦äºå£°ææä»¶ï¼.d.tsæä»¶ï¼ã
å®ä¹åºç¨çç¶æ
æä»¬éè¦å®ä¹Reduxä¿åçstateçç»æã
å建src/types/index.tsxæä»¶ï¼å®ä¿åäºç±»åçå®ä¹ï¼æä»¬å¨æ´ä¸ªç¨åºéé½å¯è½ç¨å°ã
// src/types/index.tsx
export interface StoreState {
languageName: string;
enthusiasmLevel: number;
}
è¿éæä»¬æ³è®©languageName表示åºç¨ä½¿ç¨çç¼ç¨è¯è¨ï¼ä¾å¦ï¼TypeScriptæè
JavaScriptï¼ï¼enthusiasmLevelæ¯å¯åçã
å¨åæä»¬ç第ä¸ä¸ªå®¹å¨çæ¶åï¼å°±ä¼æç½ä¸ºä»ä¹è¦ä»¤stateä¸propsç¨æä¸åã
æ·»å actions
ä¸é¢æä»¬å建è¿ä¸ªåºç¨å°è¦ååºçæ¶æ¯ç±»åï¼src/constants/index.tsxã
// src/constants/index.tsx
export const INCREMENT_ENTHUSIASM = 'INCREMENT_ENTHUSIASM';
export type INCREMENT_ENTHUSIASM = typeof INCREMENT_ENTHUSIASM;
export const DECREMENT_ENTHUSIASM = 'DECREMENT_ENTHUSIASM';
export type DECREMENT_ENTHUSIASM = typeof DECREMENT_ENTHUSIASM;
è¿éçconst/type模å¼å
许æä»¬ä»¥å®¹æè®¿é®åéæçæ¹å¼ä½¿ç¨TypeScriptçå符串åé¢éç±»åã
æ¥ä¸æ¥ï¼æä»¬å建ä¸äºactions以åå建è¿äºactionsç彿°ï¼src/actions/index.tsxã
import * as constants from '../constants'
export interface IncrementEnthusiasm {
type: constants.INCREMENT_ENTHUSIASM;
}
export interface DecrementEnthusiasm {
type: constants.DECREMENT_ENTHUSIASM;
}
export type EnthusiasmAction = IncrementEnthusiasm | DecrementEnthusiasm;
export function incrementEnthusiasm(): IncrementEnthusiasm {
return {
type: constants.INCREMENT_ENTHUSIASM
}
}
export function decrementEnthusiasm(): DecrementEnthusiasm {
return {
type: constants.DECREMENT_ENTHUSIASM
}
}
æä»¬å建äºä¸¤ä¸ªç±»åï¼å®ä»¬è´è´£å¢å æä½ååå°æä½çè¡ä¸ºã
æä»¬è¿å®ä¹äºä¸ä¸ªç±»åï¼EnthusiasmActionï¼ï¼å®æè¿°äºåªäºactionæ¯å¯ä»¥å¢å æåå°çã
æåï¼æä»¬å®ä¹äºä¸¤ä¸ªå½æ°ç¨æ¥å建å®é
çactionsã
è¿éæä¸äºæ¸ æ°ç模çï¼ä½ å¯ä»¥åè类似redux-actionsçåºã
æ·»å reducer
ç°å¨æä»¬å¯ä»¥å¼å§å第ä¸ä¸ªreduceräºï¼ Reducersæ¯å½æ°ï¼å®ä»¬è´è´£çæåºç¨stateçæ·è´ä½¿ä¹äº§çååï¼ä½å®å¹¶æ²¡æå¯ä½ç¨ã å®ä»¬æ¯ä¸ç§çº¯å½æ°ã
æä»¬çreducerå°æ¾å¨src/reducers/index.tsxæä»¶éã
å®çåè½æ¯ä¿è¯å¢å æä½ä¼è®©æå¹çº§å«å 1ï¼åå°æä½åè¦å°æå¹çº§å«å1ï¼ä½æ¯è¿ä¸ªçº§å«æ°¸è¿ä¸è½å°äº1ã
// src/reducers/index.tsx
import { EnthusiasmAction } from '../actions';
import { StoreState } from '../types/index';
import { INCREMENT_ENTHUSIASM, DECREMENT_ENTHUSIASM } from '../constants/index';
export function enthusiasm(state: StoreState, action: EnthusiasmAction): StoreState {
switch (action.type) {
case INCREMENT_ENTHUSIASM:
return { ...state, enthusiasmLevel: state.enthusiasmLevel + 1 };
case DECREMENT_ENTHUSIASM:
return { ...state, enthusiasmLevel: Math.max(1, state.enthusiasmLevel - 1) };
}
return state;
}
注ææä»¬ä½¿ç¨äºå¯¹è±¡å±å¼ï¼...stateï¼ï¼å½æ¿æ¢enthusiasmLevelæ¶ï¼å®å¯ä»¥å¯¹ç¶æè¿è¡æµ
æ·è´ã
å°enthusiasmLevel屿§æ¾å¨æ«å°¾æ¯ååå
³é®çï¼å¦åå®å°è¢«æ§çç¶æè¦çã
ä½ å¯è½æ³è¦å¯¹reduceråä¸äºæµè¯ã å 为reducersæ¯çº¯å½æ°ï¼å®ä»¬å¯ä»¥ä¼ å ¥ä»»æçæ°æ®ã é对æ¯ä¸ªè¾å ¥ï¼å¯ä»¥æµè¯reducersçæçæ°çç¶æã å¯ä»¥èè使ç¨JestçtoEqualæ¹æ³ã
å建容å¨
å¨ä½¿ç¨Reduxæ¶ï¼æä»¬å¸¸å¸¸è¦å建ç»ä»¶å容å¨ã ç»ä»¶æ¯æ°æ®æ å ³çï¼ä¸å·¥ä½å¨è¡¨ç°å±ã 容å¨é常å 裹ç»ä»¶åå ¶ä½¿ç¨çæ°æ®ï¼ç¨ä»¥æ¾ç¤ºåä¿®æ¹ç¶æã ä½ å¯ä»¥å¨è¿éé 读æ´å¤å ³äºè¿ä¸ªæ¦å¿µçç»èï¼Dan Abramovåç表ç°å±ç容å¨ç»ä»¶ã
ç°å¨æä»¬ä¿®æ¹src/components/Hello.tsxï¼è®©å®å¯ä»¥ä¿®æ¹ç¶æã
æä»¬å°æ·»å 两个å¯éçåè°å±æ§å°Propsï¼å®ä»¬å嫿¯onIncrementåonDecrementï¼
export interface Props {
name: string;
enthusiasmLevel?: number;
onIncrement?: () => void;
onDecrement?: () => void;
}
ç¶åå°è¿ä¸¤ä¸ªåè°ç»å®å°ä¸¤ä¸ªæ°æé®ä¸ï¼å°æé®æ·»å 尿们çç»ä»¶éã
function Hello({ name, enthusiasmLevel = 1, onIncrement, onDecrement }: Props) {
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
<div>
<button onClick={onDecrement}>-</button>
<button onClick={onIncrement}>+</button>
</div>
</div>
);
}
é常æ
åµä¸ï¼æä»¬åºè¯¥ç»onIncrementåonDecrementåä¸äºæµè¯ï¼å®ä»¬æ¯å¨åèªçæé®è¢«ç¹å»æ¶è°ç¨ã
è¯ä¸è¯ä»¥ä¾¿ææ¡ç¼åæµè¯ççªé¨ã
ç°å¨æä»¬çç»ä»¶æ´æ°å¥½äºï¼å¯ä»¥æå®æ¾å¨ä¸ä¸ªå®¹å¨éäºã
让æä»¬æ¥å建ä¸ä¸ªæä»¶src/containers/Hello.tsxï¼å¨å¼å§çå°æ¹ä½¿ç¨ä¸å导å
¥è¯å¥ã
import Hello from '../components/Hello';
import * as actions from '../actions/';
import { StoreState } from '../types/index';
import { connect, Dispatch } from 'react-redux';
两个å
³é®ç¹æ¯åå§çHelloç»ä»¶åreact-reduxçconnect彿°ã
connectå¯ä»¥å°æä»¬çHelloç»ä»¶è½¬æ¢æä¸ä¸ªå®¹å¨ï¼éè¿ä»¥ä¸ä¸¤ä¸ªå½æ°ï¼
mapStateToPropså°å½åstoreéçæ°æ®ä»¥æä»¬çç»ä»¶éè¦çå½¢å¼ä¼ éå°ç»ä»¶ãmapDispatchToPropså©ç¨dispatch彿°ï¼å建åè°propså°actionséå°storeã
åæ³ä¸ä¸ï¼æä»¬çåºç¨å
å«ä¸¤ä¸ªå±æ§ï¼languageNameåenthusiasmLevelã
æä»¬çHelloç»ä»¶ï¼å¸æå¾å°ä¸ä¸ªnameåä¸ä¸ªenthusiasmLevelã
mapStateToPropsä¼ä»storeå¾å°ç¸åºçæ°æ®ï¼å¦æéè¦çè¯å°é对ç»ä»¶çpropsè°æ´å®ã
ä¸é¢è®©æä»¬ç»§ç»å¾ä¸åã
export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) {
return {
enthusiasmLevel,
name: languageName,
}
}
注æmapStateToPropsä»
å建äºHelloç»ä»¶éè¦çåä¸ªå±æ§ä¸ç两个ã
æä»¬è¿æ³è¦ä¼ å
¥onIncrementåonDecrementåè°å½æ°ã
mapDispatchToPropsæ¯ä¸ä¸ªå½æ°ï¼å®éè¦ä¼ å
¥ä¸ä¸ªè°åº¦å½æ°ã
è¿ä¸ªè°åº¦å½æ°å¯ä»¥å°actionsä¼ å
¥storeæ¥è§¦åæ´æ°ï¼å æ¤æä»¬å¯ä»¥å建ä¸å¯¹åè°å½æ°ï¼å®ä»¬ä¼å¨éè¦çæ¶åè°ç¨è°åº¦å½æ°ã
export function mapDispatchToProps(dispatch: Dispatch<actions.EnthusiasmAction>) {
return {
onIncrement: () => dispatch(actions.incrementEnthusiasm()),
onDecrement: () => dispatch(actions.decrementEnthusiasm()),
}
}
æåï¼æä»¬å¯ä»¥è°ç¨connectäºã
connecté¦å
伿¥æ¶mapStateToPropsåmapDispatchToPropsï¼ç¶åè¿åå¦ä¸ä¸ªå½æ°ï¼æä»¬ç¨å®æ¥å
裹æä»¬çç»ä»¶ã
æç»ç容卿¯éè¿ä¸é¢ç代ç å®ä¹çï¼
export default connect(mapStateToProps, mapDispatchToProps)(Hello);
ç°å¨ï¼æä»¬çæä»¶åºè¯¥æ¯ä¸é¢è¿ä¸ªæ ·åï¼
// src/containers/Hello.tsx
import Hello from '../components/Hello';
import * as actions from '../actions/';
import { StoreState } from '../types/index';
import { connect, Dispatch } from 'react-redux';
export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) {
return {
enthusiasmLevel,
name: languageName,
}
}
export function mapDispatchToProps(dispatch: Dispatch<actions.EnthusiasmAction>) {
return {
onIncrement: () => dispatch(actions.incrementEnthusiasm()),
onDecrement: () => dispatch(actions.decrementEnthusiasm()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Hello);
å建store
让æä»¬åå°src/index.tsxã
è¦æææçä¸è¥¿åå°ä¸èµ·ï¼æä»¬éè¦å建ä¸ä¸ªå¸¦åå§ç¶æçstoreï¼å¹¶ç¨æä»¬ææçreducersæ¥è®¾ç½®å®ã
import { createStore } from 'redux';
import { enthusiasm } from './reducers/index';
import { StoreState } from './types/index';
const store = createStore<StoreState>(enthusiasm, {
enthusiasmLevel: 1,
languageName: 'TypeScript',
});
storeå¯è½æ£å¦ä½ æ³ç飿 ·ï¼å®æ¯æä»¬åºç¨å
¨å±ç¶æçæ ¸å¿storeã
æ¥ä¸æ¥ï¼æä»¬å°è¦ç¨./src/containers/Helloæ¥å
裹./src/components/Helloï¼ç¶å使ç¨react-reduxçProviderå°propsä¸å®¹å¨è¿éèµ·æ¥ã
æä»¬å°å¯¼å
¥å®ä»¬ï¼
import Hello from './containers/Hello';
import { Provider } from 'react-redux';
å°store以Providerç屿§å½¢å¼ä¼ å
¥ï¼
ReactDOM.render(
<Provider store={store}>
<Hello />
</Provider>,
document.getElementById('root') as HTMLElement
);
注æï¼Helloä¸åéè¦propsäºï¼å 为æä»¬ä½¿ç¨äºconnect彿°ä¸ºå
裹起æ¥çHelloç»ä»¶çpropséé
äºåºç¨çç¶æã
éåº
å¦æä½ åç°create-react-app使ä¸äºèªå®ä¹è®¾ç½®åå¾å°é¾ï¼é£ä¹ä½ å°±å¯ä»¥éæ©ä¸ä½¿ç¨å®ï¼ä½¿ç¨ä½ éè¦é ç½®ã æ¯å¦ï¼ä½ è¦æ·»å ä¸ä¸ªWebpackæä»¶ï¼ä½ å°±å¯ä»¥å©ç¨create-react-appæä¾çâejectâåè½ã
è¿è¡ï¼
npm run eject
è¿æ ·å°±å¯ä»¥äºï¼
ä½ è¦æ³¨æï¼å¨è¿è¡ejectåæå¥½ä¿åä½ ç代ç ã ä½ ä¸è½æ¤éejectå½ä»¤ï¼å æ¤éåºæä½æ¯æ°¸ä¹ æ§çé¤éä½ ä»ä¸ä¸ªè¿è¡ejectåçæäº¤æ¥æ¢å¤å·¥ç¨ã
ä¸ä¸æ¥
create-react-app带æå¾å¤å¾æ£çåè½ã
å®ä»¬ç大夿°é½å¨æä»¬å·¥ç¨çæçREADME.mdéé¢æè®°å½ï¼æä»¥å¯ä»¥ç®åé
读ä¸ä¸ã
å¦æä½ æ³å¦ä¹ æ´å¤å ³äºReduxçç¥è¯ï¼ä½ å¯ä»¥åå¾å®æ¹ç«ç¹æ¥çææ¡£ã åæ ·çï¼MobX宿¹ç«ç¹ã
å¦æä½ æ³è¦å¨æä¸ªæ¶é´ç¹ejectï¼ä½ éè¦äºè§£åå¤å ³äºWebpackçç¥è¯ã ä½ å¯ä»¥æ¥çReact & Webpackæç¨ã
ææ¶åä½ éè¦è·¯ç±åè½ã å·²ç»æä¸äºè§£å³æ¹æ¡äºï¼ä½æ¯å¯¹äºReduxå·¥ç¨æ¥è®²react-routeræ¯ææµè¡çï¼å¹¶ç»å¸¸ä¸react-router-reduxèå使ç¨ã