create-react-app
- 項(xiàng)目是用cli生成的, 目錄默認(rèn)不會(huì)有webpack配置項(xiàng)
- 但是我們有需要對(duì)其進(jìn)行改造, 比如要用 less 啊等
- 這個(gè)時(shí)候要運(yùn)行 npm run eject 將所有配置文件給暴露出來(lái) (操作不可逆)
- 然后根據(jù)自己的需要,下載 loader 修改配置
定義react組件 props 和 state 的類(lèi)型

react1.jpg
子組件向父組件輸出值
- 父組件向子組件內(nèi)部輸入一個(gè)事件, 子組件觸發(fā)這個(gè)事件,并傳參
import * as React from 'react';
import './App.less';
class ChildCom extends React.Component<IChildComProps> {
constructor(props: any) {
super(props);
this.change = this.change.bind(this);
}
change(e: any) {
this.props.change(e.target.value);
}
render() {
return <input onChange={this.change} type="text"/>
}
}
class App extends React.Component<any, IAppComState> {
constructor(props: any) {
super(props);
this.state = {
value: ''
}
this.change = this.change.bind(this)
}
public render() {
return (
<div className="app">
<ChildCom change={this.change}/>
{this.state.value}
</div>
);
}
change(e: string) {
this.setState({
value: e
})
}
}
interface IChildComProps extends React.Props<any>{
change: (v: any) => void;
}
interface IAppComState{
value: string;
}
export default App;
路由
-
error-info:you should not use link outside a router - react-router-dom 要求路由的匹配(Route)和 路由的跳轉(zhuǎn)(Link) 都要寫(xiě)在 Router里面
- 所以可將我們的整個(gè)應(yīng)用 都包裹在Router里面
做法如下:

router.jpg

route文件.jpg
<Switch>
<Route exact path='/' component={Home} /> // 設(shè)置默認(rèn)路由
<Route path='/home' component={Home} />
<Route path='/find' component={Find} />
<Route path='/recommend' component={Recommend} />
<Route path='/rank' component={Rank} />
</Switch>
-
exact精確匹配 如果不加上此標(biāo)志 所有路由匹配都會(huì)匹配到 / (表現(xiàn)是路由跳轉(zhuǎn)老是默認(rèn)路由)
動(dòng)態(tài)的打開(kāi)組件(適用彈窗,消息提醒等)
@Injectable()
export class UDynamicService {
private nameDivMap: Map<string, Element[]> = new Map();
private divNameMap: Map<Element, string> = new Map();
open<CP>(options: DynamicHelperOptions<CP>) {
const Component = options.component;
const name = Component.name;
const div = document.createElement('div');
ReactDOM.render((
<Component id={div} {...options.props} />
), div);
if (options.selector) {
options.selector.appendChild(div);
} else {
document.body.appendChild(div);
}
const arrDiv: Element[] = (this.nameDivMap.get(name) || []);
arrDiv.push(div);
this.nameDivMap.set(name, arrDiv);
this.divNameMap.set(div, name);
return div;
}
private destroyedEleAndCom(ele: Element) {
ReactDOM.unmountComponentAtNode(ele);
ele.remove();
}
private removeDivFormMap(name: string, ele: Element) {
this.divNameMap.delete(ele);
const divArr = this.nameDivMap.get(name) || [];
if (divArr.length > 0) {
removeFromArrayByCondition(divArr, (item: Element) => {
return item === ele;
});
}
}
destroyed(ele: Element, isDeleteOtherSameCom?: boolean) {
setTimeout(() => {
const name = this.divNameMap.get(ele);
if (isDeleteOtherSameCom) {
const divArr = this.nameDivMap.has(name) ? this.nameDivMap.get(name) : [];
divArr.forEach(item => {
this.destroyedEleAndCom(item);
this.removeDivFormMap(name, item);
});
} else {
this.destroyedEleAndCom(ele);
this.removeDivFormMap(name, ele);
}
}, 300);
}
}
interface DynamicHelperOptions<CP> {
// 默認(rèn)id為 displayName, 但是有種很特殊的情況, 一個(gè)組件要打開(kāi)兩次
// id?: any,
// eslint-disable-next-line no-undef
component: (props: CP) => JSX.Element; // 要打開(kāi)的組件
props?: CP;
selector?: HTMLDivElement; // 在哪打開(kāi)它
isDeleteOtherSameCom?: boolean; // 要?jiǎng)h除已經(jīng)打開(kāi)的相同的displayName的組件嗎
}
利用 context 實(shí)現(xiàn)類(lèi)似Vue的插槽分發(fā)組件
- 先看使用
// TestSlot.tsx
import React from 'react';
import { Slot } from './Slot';
import { SlotProvider } from './slotProvider';
class TestSlot extends React.Component {
render() {
return (
<div>
雅哈哈哈雅哈哈哈雅哈哈哈雅哈哈哈
<Slot name='header'></Slot>
<Slot></Slot>
<Slot name='footer'></Slot>
</div>
)
}
}
export default SlotProvider(TestSlot)
// app.tsx
import React from 'react';
import { AddOn } from './component/TestSlot/AddOn';
import SlotProvider from './component/TestSlot/TestSlot';
function App() {
return (
<div className="App">
<SlotProvider>
<AddOn slot='header'>headerheaderheader</AddOn>
<AddOn>bodyodyodyodyody</AddOn>
<AddOn slot='footer'>fotterfotterfotterfotterfotter</AddOn>
</SlotProvider>
</div>
);
}
export default App;
- 實(shí)現(xiàn)思路
SlotProvider 作為一個(gè)高階組件 實(shí)現(xiàn)
AddOn和Slot兩組件的數(shù)據(jù)流轉(zhuǎn) 這里選擇context
AddOn 組件作為中間層 本身其實(shí)啥也不干,它的作用就是為
SlotProvider提供插槽數(shù)據(jù)
Slot 組件其實(shí)就是根據(jù)自身的
slot name去獲取渲染的內(nèi)容了
- 實(shí)現(xiàn)如下
// slotProvider.tsx
import React, { ReactNode } from 'react';
import { AddOn } from './AddOn';
export const SlotContext = React.createContext<{[props: string]: ReactNode}>({
addOnRenders: {} // 用 slotName: 渲染內(nèi)容(children)的形式存放渲染數(shù)據(jù)
})
export const SlotProvider = (WrapperComponent: typeof React.Component) => {
return class extends React.Component {
addOnRenders: {[props: string]: ReactNode} = {}
render() {
// addOn 作為 SlotProvider 的子組件 這里 把它鋪平為數(shù)組 并 存放到 addOnRenders 里面
let addOnArrs: AddOn[] = React.Children.toArray(this.props.children) as any
if (addOnArrs && addOnArrs.length > 0) {
addOnArrs.forEach(item => {
let key: string = item.props.slot || '$$default' // default 作為默認(rèn)值
this.addOnRenders[key] = item.props.children
})
}
return (
<SlotContext.Provider value={this.addOnRenders as any}>
<WrapperComponent {...this.props}/>
</SlotContext.Provider>
)
}
}
}
// AddOn.tsx
import React from 'react';
export class AddOn extends React.Component<AddOnPropsObj> {
constructor(props: AddOnPropsObj) {
super(props)
}
render() {
return null
}
}
type AddOnPropsObj = {slot?: string}
// Slot.tsx
import React from 'react';
import { SlotContext } from './slotProvider';
export class Slot extends React.Component<SlotPropsObj> {
// 渲染 AddOn 緩存的 children
render() {
return (
<SlotContext.Consumer>
{(context) => {
// console.log(context, this.props)
let renderContent = context[this.props.name || '$$default']
return renderContent || null
}}
</SlotContext.Consumer>
)
}
}
type SlotPropsObj = {
name?: string
}

1584090281595.jpg
類(lèi)型定義 相關(guān)
1、 類(lèi)型 React.Component 和 typeof React.Component 又什么區(qū)別
typeof React.Component指的是React.Component類(lèi) 類(lèi)型
直接使用
React.Component指的是React.Component實(shí)例 類(lèi)型
class Test {
static aa() {}
bb() {}
}
let a: Test
a = new Test() // 正確
// a = Test // 報(bào)錯(cuò)
let b: typeof Test
// b = new Test() // 報(bào)錯(cuò)
b = Test // 正確