props
在函数式组件里面, 使用传参的形式拿到props
在es6 class语法里面, 使用this.props拿到 props
props是只读属性
function Welcome(props) {
return Hello, {props.name} ;
}
function App() {
return (
);
}
ES6 class写法
class Welcome extends React.Component {
render() {
return Hello, {this.props.name} ;
}
}
生命周期
componentDidMount 组件挂载完毕
componentWillUnmount 组件即将卸载
state
constructor(props) {
super(props);
this.state = {date: new Date()}; // 定义一个state
}
注意事项:
this.state.comment = 'Hello'; // 此代码不会重新渲染组件:
| | 正确使用方式 | --------------->
this.setState({comment: 'Hello'});
注意事项2
this.props为异步获取数据, this.state也可能是异步获取数据, 当props或state数据改变, 可能会导致数据不会更新
this.setState({
counter: this.state.counter + this.props.increment,
});
| | 正确使用方式 | --------------->
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
| | 或正确使用方式 | --------------->
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
事件绑定
基础例子:
Activate Lasers
注意事项:
不能 return false的方式 阻止默认行为 必须明确的使用 preventDefault
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
Click me
);
}
绑定this JSX 回调函数中的 this,类的方法默认是不会绑定 this 的, this 的值会是 undefined。
解决方法1: 在constructor 中显示的为 函数 使用 Bind方法绑定this
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this); // 这里绑定了this
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
{this.state.isToggleOn ? 'ON' : 'OFF'}
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
解决方法2: 函数使用es6箭头函数的方式声明
handleClick = () => {
console.log('this is:', this);
}
解决方法3:
this.handleClick(e)}>
Click me
解决方法4:
Delete Row
条件渲染
1.在方法内部使用if/else
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return ;
}
return ;
}
ReactDOM.render(
// Try changing to isLoggedIn={true}:
,
document.getElementById('root')
);
在render内部使用判断语句
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = 我是组件1
我是组件2
{button}
);
}
}
使用与运算符 &&
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
Hello!
{unreadMessages.length > 0 &&
You have {unreadMessages.length} unread messages.
}
);
}
三目运算符
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
The user is {isLoggedIn ? 'currently' : 'not'} logged in.
);
}
| | | 或这样 ------------->
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
{isLoggedIn ? (
) : (
)}
);
}
阻止组件渲染
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
Warning!
);
}
map渲染
循环的时候记得加key
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
{number}
);
return (
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
,
document.getElementById('root')
);
keys 与组件
被循环的根组件内添加key
数组元素中使用的key在其兄弟之间应该是独一无二的。然而,它们不需要是全局唯一的
return (
// 错啦!你不需要在这里指定key:
{value}
);
const listItems = numbers.map((number) =>
//错啦!元素的key应该在这里指定:
);
| | | 正确使用 |-------》
function ListItem(props) {
// 对啦!这里不需要指定key:
return {props.value} ;
}
const listItems = numbers.map((number) =>
// 又对啦!key应该在数组的上下文中被指定
);
JSX允许在大括号中嵌入任何表达式,
return (
{numbers.map((number) =>
)}
);
受控组件
input
使用value={this.state.value}绑定数据, 然后通过change事件修改state.value数据
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
);
}
}
textarea
textarea标签的使用方式跟Input一样, 不同的是, 在react中, textarea会用value属性来代替
select
Coconut选项最初由于selected属性是被选中的。在React中,并不使用之前的selected属性,而在根select标签上用value属性来表示选中项
Grapefruit
Lime
Coconut
Mango
input file标签
由于该标签的 value 属性是只读的, 所以它是 React 中的一个非受控组件。
组件交互(状态提升)
//------------------------ 父组件
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.state = {temperature: ''};
}
// 注册方法
handleCelsiusChange(temperature) {
this.setState({temperature});
}
render() {
const celsius = this.state.temperature;
return (
);
}
}
//------------------------ 子组件
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
// 触发父组件传递过来的参数的方法, 吧值传递回去
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
return (
// 接收到父组件传递过来的参数, 并赋值给自己
);
}
}
props.children 子代
通过props.children渲染组件内的内容
渲染内部所有内容
function FancyBorder(props) {
return (
{props.children}
);
}
function WelcomeDialog() {
return (
Welcome
Thank you for visiting our spacecraft!
);
}
内部内容指定渲染位置
function SplitPane(props) {
return (
{props.left}
{props.right}
);
}
function App() {
return (
}
right={
} />
);
}
jsx与react
react组件与react调用
Click Me
| | 转换 |----->
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me' // 当使用自闭合标签(
) 的时候, 里面是没有内容的,可以使用Null放在这里
)
点表示法 也可以使用对象的形式作为组件
import React from 'react';
const MyComponents = {
DatePicker: function DatePicker(props) {
return Imagine a {props.color} datepicker here.
;
}
}
function BlueDatePicker() {
return ;
}
首字母大写
import React from 'react';
// 正确!组件名应该首字母大写:
function Hello(props) {
// 正确!div 是有效的 HTML 标签:
return Hello {props.toWhat}
;
}
function HelloWorld() {
// 正确!React 能够将大写开头的标签名认为是 React 组件。
return ;
}
组件变量
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// 正确!JSX 标签名可以为大写开头的变量。
const SpecificStory = components[props.storyType];
return ;
//-------------------------------
// 错误!JSX 标签名不能为一个表达式。
return ;
}
属性中的表达式
你可以传递任何 {} 包裹的 JavaScript 表达式作为一个属性值
if 语句和 for 循环在 JavaScript 中不是表达式,因此它们不能直接在 JSX 中使用,但是你可以将它们放在周围的代码中。
function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = even ;
} else {
description = odd ;
}
return {props.number} is an {description} number
;
}
=========
=======
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return ;
}
渲染时, 布尔值, Null, undefined 被忽略, 下面的都等价
{false}
{null}
{undefined}
{true}
propTypes检测类型
限制类型
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
Hello, {this.props.name}
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
具体验证例子请打开该链接 https://react.docschina.org/docs/typechecking-with-proptypes.html
限制单个子代
import PropTypes from 'prop-types';
class MyComponent extends React.Component {
render() {
// This must be exactly one element or it will warn.
const children = this.props.children;
return (
{children}
);
}
}
MyComponent.propTypes = {
children: PropTypes.element.isRequired
};
属性默认值
// 为属性指定默认值:
Greeting.defaultProps = {
name: 'Stranger'
};
refs
你不能在函数式组件上使用 ref 属性
ref 的更新会发生在componentDidMount 或 componentDidUpdate 生命周期钩子之前 使用 React.createRef() 创建 refs,通过 ref 属性来获得 React 元素
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return
;
}
}
通过this.myRef.current 拿到dom元素
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// 创建 ref 存储 textInput DOM 元素
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// 直接使用原生 API 使 text 输入框获得焦点
// 注意:通过 "current" 取得 DOM 节点
this.textInput.current.focus();
}
render() {
// 告诉 React 我们想把 ref 关联到构造器里创建的 `textInput` 上
return (
);
}
}
回调 Refs
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
// 这里的回调 把当前元素赋值给this.textInput
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// 直接使用原生 API 使 text 输入框获得焦点
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// 渲染后文本框自动获得焦点
this.focusTextInput();
}
render() {
// 使用 `ref` 的回调将 text 输入框的 DOM 节点存储到 React
// 实例上(比如 this.textInput)
return (
);
}
}
非受控组件
基础用法
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
);
}
}
默认值 defaultValue
你希望 React 可以为其指定初始值,但不再控制后续更新。 你可以指定一个 defaultValue 属性而不是 value 同样, 和 支持 defaultChecked, 和
this.input = input} />
Context
Context 可以用来跨子组件给更下级的组件传参
创建一个Context
Provider 作为传递组件的根组件
Consumer 作为Provider 下的接收参数的组件Consumer
const {Provider, Consumer} = React.createContext(defaultValue);
接收一个 value 属性传递给 Provider 的后代 Consumers。一个 Provider 可以联系到多个 Consumers。
// 这里接收一个函数, 传递值为 Provider的value
{value => /* render something based on the context value */}
示例
export const themes = {
light: {
foreground: '#ffffff',
background: '#222222',
},
dark: {
foreground: '#000000',
background: '#eeeeee',
},
};
export const ThemeContext = React.createContext(
themes.dark // 默认值
);
import {ThemeContext} from './theme-context';
function ThemedButton(props) {
return (
// 定义 Consumer
{theme => (
)}
);
}
export default ThemedButton;
import {ThemeContext, themes} from './theme-context';
import ThemedButton from './themed-button';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
theme: themes.light,
};
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
}
render() {
return (
// 定义 Provider
// 点击切换Provider.value
);
}
}
ReactDOM.render( , document.root);
Fragments 空的 JSX 标签:
作用: 当渲染td的时候, 需要一个根组件, 但却不能随便使用, 因此可以使用一个空标签代替;
class Columns extends React.Component {
render() {
return (
Hello
World
);
}
}
| | | 输出结果 |- -------->
与<>
== <>
Fragment的属性
key 是唯一可以传递给 Fragment 的属性