是一种 JS 和 HTML 混合的语法,将组件的结构、数据甚至样式都聚合在一起定义组件
ReactDOM.render(<h1>Hello</h1>, document.getElementById("root"));
- JSX 其实只是一种语法糖,最终会通过 babeljs 转译成 createElement 语法
- React 元素是构成 React 应用的最小单位
- React 元素用来描述你在屏幕上看到的内容
React元素事实上是普通的JS对象,ReactDOM 来确保浏览器中的 DOM 数据和 React 元素保持一致
// jsx
<h1 className="title" style={{color:'red'}}>hello</h1>
// 编译后
React.createElement("h1", {
className: "title",
style: {
color: 'red'
}
}, "hello");
// createElement的结果
{
type:'h1',
props:{
className: "title",
style: {
color: 'red'
}
},
children:"hello"
}
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="zhufengjiagou" />;
ReactDOM.render(element, document.getElementById("root"));
不可修改//纯函数
function sum(a, b) {
return a + b;
}
//非纯函数
function withdraw(account, amount) {
account.total -= amount;
}
props 是只读的,我们无法在方法中修改他的值,但是可以给默认值或者设置一些规则(例如:设置是否必须传递以及传递的类型等)
// 给props设置一些默认值
static defaultProps = {
lx: '系统提示'
};
要在组件的 props 上进行类型检查,你只需配置特定的 propTypes 属性
import PropTypes from 'prop-types'; // 第三方插件
static propTypes = {
con: PropTypes.string.isRequired //=> 不仅传递的内容是字符串,并且还必须传递
};
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>
);
}
}
// 错误
this.state.comment = 'Hello';
// 正确
this.setState({comment: 'Hello'});
// 构造函数是唯一可以给 this.state 赋值的地方
constructor() {
this.state = {date: new Date()};
}
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 0,
};
}
handleClick = () => {
/**
*
* 下面例子这样处理计算是会被合并,执行了2次+1(结果应该是2),但是结果是1
*
*/
// this.setState({number:this.state.number+1});
// console.log(this.state.number); // 0
// this.setState({number:this.state.number+1});
// console.log(this.state.number); // 0
// 解决办法把它改成函数
this.setState((state) => ({ number: state.number + 1 }));
this.setState((state) => ({ number: state.number + 1 }));
};
render() {
return (
<div>
<p> {this.state.number} </p>
<button onClick={this.handleClick}>+</button>
</div>
);
}
}
// 例如,你的 state 包含几个独立的变量:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
// 然后你可以分别调用 setState() 来单独地更新它们:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
- 只有类组件才有生命周期
- hooks 出来之后有 useEffect 副作用操作的函数类似 class 的生命周期
- 你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

(当组件的状态发生改变 setState或者传递给组件的属性发生改变 重新调用组件传递不同的属性 都会引发render重新执行渲染)默认true (允许则执行后面函数,不允许直接结束即可)原有渲染的内容是不消失的,只不过以后不能基于数据改变视图了class Counter extends React.Component<Props, State> {
// 他会比较两个状态相等就不会刷新视图 PureComponent是浅比较
/* 加载顺序 */
// defaultProps
// constructor
// componentWillMount
// render
// componentDidMount
// 状态(state)更新会触发的
// shouldComponentUpdate nextProps, nextState=>boolean
// componentWillUpdate
// componentDidUpdate
// 属性(props)更新
// componentWillReceiveProps newProps
// 卸载
// componentWillUnmount
/* 如果有子组件 */
// 1.constructor构造函数
// 2.组件将要加载 componentWillMount
// 3.render
// 子组件的初始化 => child constructor
// 子组件挂载之前 => child componentWillMount
// 子组件渲染DOM => child-render
// 子组件挂载完成 => child componentDidMount
// 4.组件挂载完成 componentDidMount
/* 当父组件状态变化 */
// 5.组件是否更新 shouldComponentUpdate
// 6.组件将要更新 componentWillUpdate
// 3.render 父组件渲染
// 子组件监听到props变化 => child componentWillReceiveProps
// 子组件渲染之前 => componentWillUpdate
// 子组件渲染DOM => child-render
// 子组件渲染之后 => componentDidUpdate
// 7.组件完成更新 componentDidUpdate
}

查看例子(1)查看例子(2)例子(1):
// 前面要加static,nextProps新的属性对象,prevState代表老的状态对象
static getDerivedStateFromProps(nextProps, prevState){
const { number } = nextProps;
// 当传入的type发生变化的时候,更新state
if (number % 2 === 0) {
return { number: number * 2 };
} else {
return { number: number * 3 };
}
}
例子(2):旧版的react做固定滚动列表的时候存在个问题,无法固定当前位置(DOM不断的添加scrollTop会不断被卷去)如下图:
我们通过新版中的getSnapshotBeforeUpdate 在render之后获取DOM信息在componentDidUpdate更新后再去设置scrollTop值来解决这个问题
//很关键的,我们获取当前rootNode的scrollHeight,传到componentDidUpdate 的参数perScrollHeight
getSnapshotBeforeUpdate() {
return this.wrapper.current.scrollHeight;
}
componentDidUpdate(pervProps, pervState, prevScrollHeight) {
//当前向上卷去的高度
const curScrollTop = this.wrapper.current.scrollTop;
//当前向上卷去的高度加上增加的内容高度
this.wrapper.current.scrollTop = curScrollTop + (this.wrapper.current.scrollHeight - prevScrollHeight);
}

false 的方式阻止默认行为。你必须显式的使用 preventDefaultclass Link extends React.Component {
handleClick(e) {
e.preventDefault();
console.log("The link was clicked.");
}
render() {
return (
<a href="http://www.baidu.com" onClick={this.handleClick}>
Click me
</a>
);
}
}
class LoggingButton extends React.Component {
handleClick() {
console.log("this is:", this);
}
handleClick1 = () => {
console.log("this is:", this);
};
render() {
//onClick={this.handleClick.bind(this)
return (
<button onClick={(event) => this.handleClick(event)}>Click me</button>
);
}
}
class LoggingButton extends React.Component {
handleClick1 = (id, event) => {
console.log("id:", id);
};
render() {
return (
<>
<button onClick={(event) => this.handleClick("1", event)}>
Click me
</button>
<button onClick={this.handleClick.bind(this, "1")}>Click me</button>
</>
);
}
}
字符串class Sum extends React.Component {
handleAdd = (event: React.MouseEvent) => {
let a = this.refs.a.value;
let b = this.refs.b.value;
this.refs.c.value = a + b;
};
render() {
return (
<>
<input ref="a" />+<input ref="b" />
<button onClick={this.handleAdd}>=</button>
<input ref="c" />
</>
);
}
}
函数class Sum extends React.Component {
handleAdd = (event) => {
let a = this.a.value;
let b = this.b.value;
this.result.value = a + b;
};
render() {
return (
<>
<input ref={(ref) => (this.a = ref)} />+
<input ref={(ref) => (this.b = ref)} />
<button onClick={this.handleAdd}>=</button>
<input ref={(ref) => (this.result = ref)} />
</>
);
}
}
currentclass Sum extends React.Component {
constructor(props) {
super(props);
this.a = React.createRef();
this.b = React.createRef();
this.result = React.createRef();
}
handleAdd = () => {
let a = this.a.current.value;
let b = this.b.current.value;
this.result.current.value = a + b;
};
render() {
return (
<>
<input ref={this.a} />+<input ref={this.b} />
<button onClick={this.handleAdd}>=</button>
<input ref={this.result} />
</>
);
}
}
currentclass Form extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();
}
getFocus = () => {
this.input.current.getFocus();
};
render() {
return (
<>
<TextInput ref={this.input} />
<button onClick={this.getFocus}>获得焦点</button>
</>
);
}
}
class TextInput extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();
}
getFocus = () => {
this.input.current.focus();
};
render() {
return <input ref={this.input} />;
}
}
function CustomTextInput(props) {
// 这里必须声明 textInput,这样 ref 才可以引用它
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input type="text" ref={textInput} />
<input type="button" value="Focus the text input" onClick={handleClick} />
</div>
);
}
class Form extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();
}
getFocus = () => {
this.input.current.getFocus();
};
render() {
return (
<>
<TextInput ref={this.input} />
<button onClick={this.getFocus}>获得焦点</button>
</>
);
}
}
// Function components cannot be given refs. Attempts to
// access this ref will fail. Did you mean to use React.forwardRef()?
function TextInput() {
return <input />;
}
// forwardRef转发函数组件
const TextInput = React.forwardRef((props, ref) => <input ref={ref} />);
class Form extends React.Component {
input;
constructor(props) {
super(props);
this.input = React.createRef();
}
getFocus = () => {
console.log(this.input.current);
this.input.current.focus();
};
render() {
return (
<>
<TextInput ref={this.input} />
<button onClick={this.getFocus}>获得焦点</button>
</>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// 尝试更改为 isLoggedIn={true}:
<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>
);
}
const messages = ["React", "Re: React", "Re:Re: React"];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById("root")
);
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
// 这样切换时会直接销毁组件,导致重复的走一遍组件生命周期(不推荐)
function WarningBanner(props) {
return <div className="warning">Warning!</div>;
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
showWarning: false,
};
}
render() {
return <div>{this.state.showWaring ? <WarningBanner /> : null}</div>;
}
}
// 使用组件返回null并不会影响组件生命周期(推荐)
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return <div className="warning">Warning!</div>;
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = { showWarning: true };
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState((state) => ({
showWarning: !state.showWarning,
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? "Hide" : "Show"}
</button>
</div>
);
}
}
<li>标签,返回新数组进行赋值,然后渲染进 DOMconst numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => <li>{number}</li>);
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => (
<li key={number.toString()}>{number}</li>
));
function FancyBorder(props) {
return (
<div className={"FancyBorder FancyBorder-" + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">Welcome</h1>
<p className="Dialog-message">Thank you for visiting our spacecraft!</p>
</FancyBorder>
);
}
在某些场景下,你想在整个组件树中传递数据,但却不想手动地在每一层传递属性。你可以直接在 React 中使用强大的 contextAPI 解决上述问题
数据是通过 props 属性自上而下(由父及子)进行传递的,但是这种做法对于某些类型的属性而言是极其繁琐的(例如:地区/偏好/UI 主题等)属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。举个例子:
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
// 因为必须将这个值层层传递所有组件。
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
使用 context, 我们可以避免通过中间元素传递 props:
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext("light");
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee",
},
dark: {
foreground: "#ffffff",
background: "#222222",
},
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}