从React15的文档开始,照着文档走一遍
1.创建应用
安装create-react-app
1 2 3 4 5 6 7
| npm install -g create-react-app
create-react-app react-demo
cd react-demo
npm start
|
降级react
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| { "name": "react-demo", "version": "0.1.0", "private": true, "dependencies": { "@testing-library/dom": "^6.1.5", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^10.4.1", "react": "^15.6.2", "react-dom": "^15.6.2", "react-scripts": "3.4.4", "web-vitals": "^2.1.4" }, ...... }
|
2.JSX
js拓展语法,将dom作为js的变量,使用{}在dom嵌入js变量。
1 2 3 4 5 6
| const element = <img src={user.avatarUrl}></img>; const element = ( <div> <h1>你好。</h1> </div> );
|
2.1将元素渲染到DOM中/更新元素
通过创建一个新元素来更新元素
1 2 3 4 5 6 7 8 9 10 11 12 13
| function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);
|
2.2组件
2.2.1函数式组件
1 2 3
| function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
|
2.2.1类组件
1 2 3 4 5
| class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
|
2.2.3渲染自定义组件
1 2 3 4 5 6
| const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') );
|
- 自定义组件可以并列和嵌套
- 组件的props不可以修改
3.状态和生命周期
State 与 props 类似,但它是私有的并且完全由组件控制。
3.1 函数式组件转换成类组件的步骤
- 创建一个具有相同名称的 ES6 类来扩展
React.Component
- 向其中添加一个名为
render() 的空方法
- 将函数主体移至
render() 方法中
- 在
render() 主体中用 this.props 替换 props
- 删除剩余的空函数声明
1 2 3 4 5 6 7 8 9 10
| class Clock extends React.Component { render() { return ( <div> <h1>泥嚎!</h1> <h2>{this.props.date.toLocaleTimeString()}</h2> </div> ); } }
|
3.2 向类组件添加state
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
ReactDOM.render( <Clock />, document.getElementById('root') );
|
3.3 向类组件添加生命周期方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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({ date: new Date() }); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
|
注意:
- 不要直接修改
state,更新state使用setState()
state更新可能为异步
1 2 3 4 5 6 7
|
this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
|
4.处理事件
- React 事件使用驼峰命名法,而不是小写
- 使用 JSX来传递事件
- 必须显式调用preventDefault()来阻止默认事件
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
| function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); }
return ( <a href="#" onClick={handleClick}> Click me </a> ); }
class LoggingButton extends React.Component { handleClick = () => { console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}>Click me</button> ); } }
class Toggle extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}>Click me</button> ); } }
|
5.条件渲染
React使用js中的if语句来判断控制显示哪些组件,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } ReactDOM.render( <Greeting isLoggedIn={false} />, document.getElementById('root') );
function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2>You have {unreadMessages.length} unread messages.</h2> } </div> ); }
|
5.1阻止组件渲染
1 2 3 4 5 6 7 8 9
| function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning">Warning!</div> ); }
|
6.列表渲染
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
| function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}>{number}</li> ); return ( <ul>{listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') );
function NumberList(props) { const numbers = props.numbers; return ( <ul> {numbers.map((number) => <ListItem key={number.toString()} value={number} /> )} </ul> ); }
|
7.表单
7.1受控组件
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
| class NameForm extends React.Component { constructor(props) { super(props); this.state = { input1: '', input2:'', textarea1:'', select1:'', }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { const target = event.target; const name = target.name; this.setState({ [name]: value }); } handleSubmit(event) { alert('A name was submitted: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label>Name: //输入框 <input name="input1" type="text" value={this.state.input1} onChange={this.handleChange} /> </label> //多选框 <input name="input2" type="checkbox" checked={this.state.input2} onChange={this.handleInputChange} /> //textarea <textarea name="textarea1" value={this.state.textarea1} onChange={this.handleChange} /> //下拉框 <select name="select1" value={this.state.select1} onChange={this.handleChange}> <option value="grapefruit">Grapefruit</option> </select> //提交按钮 <input type="submit" value="Submit" /> </form> ); } }
|
8.状态提升/组合继承
8.1 状态提升
- 当两个兄弟组件需要根据同一个变量来改变显示时,将共同变量定义到最近父组件的state中。
- 由于props是只读的,单向传递。所以父组件需要定义一个修改此共同变量的方法,并将此方法传递给两个兄弟组件。兄弟组件调用此方法来修改共同变量
8.2 组合继承
8.2.1 插槽
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
| function FancyBorder(props) { return ( <div> {props.children} </div> ); } function WelcomeDialog() { return ( <FancyBorder> <h1>Welcome</h1> </FancyBorder> ); }
function SplitPane(props) { return ( <div className="flexBox"> <div> {props.left} </div> <div> {props.right} </div> </div> ); } function App() { return ( <SplitPane left={ <div>left</div> } right={ <div>right</div> } /> ); }
|
8.2.2 实例化组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function Dialog(props) { return ( <div className="Dialog-Wrap"> <h1 className="Dialog-title"> {props.title} </h1> <p className="Dialog-message"> {props.message} </p> </div> ); }
function WelcomeDialog() { return ( <Dialog title="Welcome" message="Thank you for visiting our spacecraft!" /> ); }
|
9.深入JSX
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React from 'react'; import { PhotoStory, VideoStory } from './stories';
const MyComponents = { DatePicker: function DatePicker(props) { return <div>Imagine a {props.color} datepicker here.</div>; } } function BlueDatePicker() { return <MyComponents.DatePicker color="blue" />; }
const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { const SpecificStory = components[props.storyType]; return <SpecificStory story={props.story} />; }
|
10.ref
可以使用ref来引用子组件或DOM元素的实例。
下面的代码this.textInput就是CustomTextInput组件的实例,在DOM挂载后的生命周期中调用了CustomTextInput组件的focusTextInput方法
注意:只有类组件,ref才会生效
1 2 3 4 5 6 7 8 9 10 11
| class AutoFocusTextInput extends React.Component { componentDidMount() { this.textInput.focusTextInput(); } render() { return ( <CustomTextInput ref={ input => this.textInput = input; } /> );
}
|
将上述代码input => this.textInput = input;暂时起名叫做引用DOM函数。
使用props由父组件将引用DOM函数传递给子组件的方式,可以使父组件获取到子组件的DOM实例。
这种方法类组件和函数式组件都可以使用,且可以多级组件传递。
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
function Parent(props) {
return (
<div>
My input: <CustomTextInput inputRef={props.inputRef} />
</div>
);
}
class Grandparent extends React.Component {
render() {
return (
<Parent
inputRef={el => this.inputElement = el}
/>
);
}
}