JSX模板
以.jsx为后缀的标记文件,用以书写混合js逻辑的dom模板
example.jsx, jsx文件经Babel编译为js运行
1 2 3 4 5 6 7
| const JSX = ( <div className="myDiv"> <h1>Hello World</h1> <p>Lets render this to the DOM</p> </div> ); ReactDOM.render(JSX,document.getElementById("challenge-node"))
|
- 用{}包含js代码,包括变量和相应方法
- 用className绑定class样式
除了jsx标记之外,还有createElement创建React组件的方法
1 2 3 4 5 6
|
const element = React.createElement('div', { className: 'greeting' }, 'Hello World!')
<element />
|
注释
use the syntax {/**/}
表单
1 2 3 4 5 6 7 8 9 10 11
| render() { return ( <form onSubmit={this.handleSubmit}> <label> 文章: <textarea value={this.state.value} onChange={this.handleChange} /> </label> <input type="submit" value="提交" /> </form> ); }
|
ES6语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import { Component } from 'react'
class App extends Component { constructor(props) { super(props); } render() { return ( <div> <h1>Here is Entry</h1> {} <ChildComponent /> </div> ); } };
const ChildComponent = () => { return ( <div> <p>I am the child</p> </div> ); };
ReactDOM.render(<App />, document.getElementById("app"))
|
子组件传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const CurrentDate = (props) => { return ( <div> <p>The current date is: {props.date}</p> </div> ); }; const Data = ()=>{ return new Date().toLocaleString(); } class Calendar extends React.Component { constructor(props) { super(props); } render() { return ( <div> <h3>What date is it?</h3> <CurrentDate date={Date()}/> <p>{this.props.name}</p> </div> ); } };
|
使用函数表达式不需要this指针而class定义是要的(ES6 ()=>{}不创建this)
另外设置默认参数:ComponentA.defaultProps = {name:’New Component’}
更多组件通信见React组件交互
参数校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| MyComponent.propTypes = { optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol,
optionalNode: PropTypes.node,
optionalElement: PropTypes.element,
optionalElementType: PropTypes.elementType,
optionalMessage: PropTypes.instanceOf(Message),
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
optionalUnion: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]),
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }), optionalObjectWithStrictShape: PropTypes.exact({ name: PropTypes.string, quantity: PropTypes.number }),
requiredFunc: PropTypes.func.isRequired,
requiredAny: PropTypes.any.isRequired,
customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } },
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) { if (!/matchme/.test(propValue[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }) };
|
使用 PropTypes 进行类型检查
state及组件生命周期
>
props是父组件传入的只读参数,state是组件自身的动态的状态
为了正确地构建组件,需要找出组件模型所需的 state 的最小表示,其他所有数据根据该state计算出。React哲学
props是传入参数,而state是组件内部表征状态的对象,往往在构造函数中,根据props初始化state
组件状态更新使用setState,函数触发组件的重新渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); } tick=()=>{this.setState((pre)=>({date:new Date()}))}
render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
|
bind ‘this’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class MyComponent extends React.Component { constructor(props) { super(props); this.state = { text: "Hello" }; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState({ text: "You clicked!" }); } render() { return ( <div> <button onClick={this.handleClick}>Click Me</button> <h1>{this.state.text}</h1> </div> ); } };
|
为什么要bind(this) 因为在严格模式下(React内部执行严格模式)函数在对象外被调用时 this 并不指向对象,事实上React组件的响应方法不‘生成’this(即undefined) 这与非严格模式下的html原生事件响应不同(非严格模式下事件会指向window) 原因在于React事件并不真实作用于dom节点,而是编译时生成的独立响应树(事件委托机制)
如果handleClick需要处理组件属性 必须onClick={this.handleClick.bind(this)} 或在构造方法中提前绑定
条件渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| render() { const isLoggedIn = this.state.isLoggedIn; let button; if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; }
return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> ); }
|
与运算&&
1
| {flag && <toggleComponent />}
|
三目运算
1 2 3 4 5 6 7 8 9 10
| render() { const isLoggedIn = this.state.isLoggedIn;
return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {isLoggedIn?<LogoutButton onClick={this.handleLogoutClick} />:<LoginButton onClick={this.handleLoginClick} />} </div> ); }
|
继承
没有继承!
在render return中组合子组件提供了清晰而安全地定制组件外观和行为的灵活方式,没有需要使用继承来构建组件层次的情况。React Docs:组合 vs 继承
createRef onRef useRef
子组件实例化回调函数,用以获取子组件对象
useRef 仅能用在 FunctionComponent,createRef 仅能用在 ClassComponent。
组件的对象会随组件的更新而刷新,但useRef返回的对象不会随着组件的更新而重新构建,像引入的全局变量,且随组件的销毁而释放,不需手动销毁
Fragment
相当于Angular的template,插入子组件不生成额外的元素(如div),可以省略为<>>
ReactDOMServer
1 2 3 4 5 6 7 8 9
| class App extends React.Component { constructor(props) { super(props); } render() { return <div/> } }; ReactDOMServer.renderToString(<App />);
|
服务端渲染(SSR),生成dom的html字符串,实现SEO优化
Create React App
这是一个package create-react-app, 如angular-cli,和vue-cli中包含的命令工具(这里封装的命令是react-scripts, 见package.json中的scripts),用以创建基于React的完整应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| my-app/ node_modules/ public/ index.html favicon.ico src/ App.css App.js App.test.js index.css index.js logo.svg README.md package.json
```js
#### 关于typescript
```js yarn add --dev typescript
|
在tsconfig.json中配置typescript编译器(略)
使用tsx文件书写包含jsx代码的typescript代码
使用tsc命令编译
添加@types/react, vs code 右下角TypeScript版本选择4..-pnpify
list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| const ArrayComponent = () => { const array = ['John', 'Robbie', 'Tony'] return ( <> {array.map((name, index)=><Item key={index} >{name}</Item>)} <> ) }
```js
需要指出的是 key 并不是 Item定义的Props,在Item组件内部访问key会得到undefined [Github Issue: this.key seems to always be undefined inside a React component ](https://github.com/facebook/react/issues/2429#issuecomment-61008642)
#### Interview Questions > > 类组件 vs 函数组件
> props不可变性
> React hook 闭包陷阱 闭包内缓存state 导致定时器时间间隔内取到的都是上一次的值
```js function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setTimeout(() => { setCount(count + 1); }, 1000); }; const handleReset = () => { setCount(0); }; return ( <div> <p>Count: {count}</p> <button onClick={handleClick}>Increment</button> <button onClick={handleReset}>Reset</button> </div> ); }
|
setCount(count => (count + 1))可以避免