скажи это прямо
Эта статья является кратким изложением и записью моего недавнего изучения React. Я старался изо всех сил записывать каждый образец кода и старался писать его простым и понятным способом. Это также можно рассматривать как упражнение моих разговорных способностей. Говорят, что я буду учиться быстрее, когда я говорить с другими, поэтому я написал эту статью.
Если есть ошибка или что-то другое, я также надеюсь, что все критические замечания правильны.
Не закончив полностью, воспользовавшись отсутствием школы, я воспользуюсь временем, чтобы учиться и обновлять
5.9 Базовое использование обновления Saga и случай
порталКажется, это не работает, это может быть локально...
Обновите использование некоторых функциональных компонентов, предоставьте основные идеи и примеры
5.10 Обновление Хуки были обновлены
Публичный аккаунт Ахена
JSX
Введение
Некоторые теги, написанные на JS, называются синтаксисом JSX.
базовая грамматика
Не нужно ставить кавычки при возврате
import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg';
// 基本
const jsx = <h1>hello react</h1>
// 变量
const name = '123456'
const jsxName = <h1>{ name }</h1>
// 函数
const user = { firstName: 'tom', lastName: 'jerry' };
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const jsxFunction = <h2>{ formatName(user) }</h2>
// 对象
const greet = <p>hello, Jerry</p>
const jsxObject = <h2>{ greet }</h2>
// 三元表达式
const showTitle = true;
const title = showTitle ? <h2>{ name }</h2> : null;
const jsxOthers = (<div>{
/* 条件语句句 */
} { title }
</div>);
// 循环
const arr = [1, 2, 3].map(num => <li key={ num }>{ num } </li>)
const jsxArray = (<div> {/* 数组 */ }
<ul>{ arr }</ul>
</div>)
// 注意
const jsxAtt = (<div>
{
/* 属性:静态值⽤用双引号,动态值⽤用花括号;class、for等 要特殊处理理。
* 像class需要写成className,for需要写成htmlFor,并且属性名需要采用驼峰命名法
* */
}
<img src={ logo } style={ { width: 100 } } className="img"/></div>)
// 函数传参
function greeting(name) {
if (name) {
return <h1>Hello, {name}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
let name2 = '测试';
const element = greeting(name2);
// 循环
let names = ['张三','李四','王五'];
let elements = [];
for(let i=0;i<names.length;i++){
elements.push(<li>{names[i]}</li>);
}
export { jsx, jsxName, jsxFunction, jsxObject, jsxOthers, jsxArray,jsxAtt,element,elements }
компоненты
Пользовательский интерфейс можно разделить на несколько независимых повторно используемых компонентов.
компонент класса
Компоненты класса обычно имеютсостояниеа такжеЖизненный цикл,Унаследовано от компонента, реализовать метод рендеринга
базовый
import React, { Component } from 'react';
class Home extends Component {
render() {
return <h1>Hello, Class Components</h1>;
}
}
Управление состоянием в компонентах класса (экземпляр секундомера)
import React, { Component } from 'react';
class State extends Component {
// 使用state属性维护状态,在构造函数中初始化状态
constructor(props) {
super(props);
this.state = { date: new Date(), number: 0 };
// this.changeNumber = this.changeNumber.bind(this)
}
// 组件挂载时启动定时器每秒更新状态
componentDidMount() {
this.timerID = setInterval(() => {
// 使⽤用setState方法更新状态
this.setState({ date: new Date() });
}, 1000);
}
// 组件卸载时停止定时器
componentWillUnmount() {
clearInterval(this.timerID);
}
// 这里不绑定this 会报错 两种方法 第一种 用箭头函数。第二种在constructor进行绑定声明
// setState是异步执行的。执行这个结果是 log 是2不是3 证明是异步执行
changeNumber = () => {
this.setState({ number: this.state.number + 1 } )
this.setState({ number: this.state.number + 2 } )
// 这里不能进行直接修改
console.log(this.state.number)
}
// 同步改变
changeNumber2 = ()=> {
this.setState((nextState)=>{ return { number: nextState.number + 1 } } )
this.setState((nextState)=>{ return { number: nextState.number + 2 } } )
console.log(this.state.number)
// 使用定时器
// setTimeout(() => { this.setState((nextState)=>{ return { number: nextState.number + 1 } } ); console.log(this.state.counter); }, 0);
// 原生事件中修改状态
// componentDidMount(){ document.body.addEventListener('click', this.setState((nextState)=>{ return { number: nextState.number + 1 } } ), false) }
}
render() {
return (
<div>
{ this.state.date.toLocaleTimeString() }
<p>{ this.state.number }</p>
<p>setState是异步执行的</p>
<button onClick={this.changeNumber}>异步改变number</button>
<button onClick={this.changeNumber2}>同步改变number</button>
</div>
)
}
}
Настройки и изменения статуса
существуетconstructor
Установить состояние. использоватьsetState
изменить состояние. Пример, как указано выше
О setState
Обратите внимание, что setStateасинхронныйиз! ! ! !
Как это доказать?
см. пример кодаchangeNumber
описание и реализация
Как добиться синхронизации?
Существует три распространенных метода:
- Передача параметров с помощью функций
- Используйте таймер
- Непосредственно найдите элемент DOM и измените его напрямую
Все три вышеуказанных метода можно найти в примере кода.
эта привязка функции
Обычно есть два метода
- существует
constructor
используется внутриbind
привязка метода. Напримерthis.changeNumber = this.changeNumber.bind(this)
- Используйте стрелочные функции ES6. Пример
changeNumber = () => { // 执行代码 }
. (** Объяснение причины: ** Функция стрелки по умолчанию использует эту точку объекта в контексте, когда он определен)
функциональный компонент
базовый
import React from "react";
import User from "./pages/User";
function App() {
return (
<div>
<User />
</div>
);
}
export default App;
позжеreact HOOKs
подробно объясним
компонент связи
propsСвязь
применять кВзаимодействие компонентов родитель-потомок
// index.js
ReactDOM.render(<App title="测试代码" />, document.querySelector('#root'));
// App.js
<h2>{this.props.title}</h2> // 输出为 测试代码
Реквизит только для чтения
Оригинальные слова официального сайта: Компонент никогда не должен изменять свои собственные реквизиты, объявленные с помощью функции или через класс.
повышение статуса
официальное объяснение: часто несколько компонентов должны отражать одни и те же измененные данные, и в этом случае мы рекомендуем продвигать общее состояние к ближайшему общему родительскому компоненту.
пример кода
import React from 'react'
class Child_1 extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<h1>{this.props.value+'child 1'}</h1>
</div>
)
}
}
class Child_2 extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<h1>{this.props.value+'child 2'}</h1>
</div>
)
}
}
class Parent extends React.Component {
constructor(props){
super(props)
this.state = {
txt:"我是父组件"
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e){
this.setState({
txt:e.target.value
})
}
render(){
return (
<div>
<input type="text" value={this.state.txt} onChange={this.handleChange}/>
<p>{this.state.txt}</p>
<Child_1 value={this.state.txt}/>
<Child_2 value={this.state.txt}/>
</div>
)
}
}
export default Parent
контекст для межкомпонентного взаимодействия
Связь между компонентами в иерархии
однослойная передача
// ContextFather.js
import React from 'react';
import ContextChild from './child';
const Context = React.createContext()
const Provider = Context.Provider
const Consumer = Context.Consumer
const data = {
name: '我是father数据',
}
function ContextApp() {
return (
<div>
<Provider value={ data }>
<Consumer>
{
value => <ContextChild {...value}/>
}
</Consumer>
</Provider>
</div>
)
}
export default ContextApp
// child.js
import React, { Component } from 'react';
class ContextChild extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props)
return (<h1>hello {this.props.name}</h1>);
}
}
export default ContextChild
Таким образом, вы можете получить пройденныйname
стоимость
многослойный перенос
// Context.js
import React from 'react';
const Context = React.createContext()
const Provider = Context.Provider
const Consumer = Context.Consumer
export {Provider,Consumer}
// ContextFather
import React from 'react';
import ContextChild from './child';
import {Consumer,Provider} from './Context';
const data = {
name: '我是father数据',
}
function ContextApp() {
return (
<div>
<Provider value={ data }>
// 这个层级不需要就不用写 Consumer
<ContextChild/>
</Provider>
</div>
)
}
export default ContextApp
// child.js
import React, { Component } from 'react';
import ContextChild2 from './child2';
import {Consumer} from './Context';
class ContextChild extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>我是第一层</h1>
// 这里获取的是一个空值,没有数据
<h2>{this.props.name}</h2>
// 这个层级需要就吧数据传递过去
<Consumer>
{
value => <ContextChild2 {...value}/>
}
</Consumer>
</div>
)
}
}
export default ContextChild
// child2.js
import React, { Component } from 'react';
class ContextChild2 extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props)
console.log('我是第二层的')
return (
<div>
<h1>我是第二层的</h1>
<h1>{this.props.name}</h1>
</div>
);
}
}
export default ContextChild2
обращать внимание
- Не может быть несколько провайдеров, например в каждом файле напишите
Context.js
заявление внутри. данные не будут получены.Значения, предоставляемые разными поставщиками, несовместимы - В официальной документации React,
Context
Он классифицируется как Advanced и относится к расширенному API React, но официально не рекомендуется использовать Context в стабильной версии приложения. Однако это не означает, что нам не нужно обращать внимание на контекст. Фактически, многие отличные элементы реагирования завершают свои собственные функции через контекст, такие как React-redux
редукс похож на Vuex
Тех, кто не изучил Vue, можно понять какглобальное управление состоянием, то есть получить к нему доступ можно где угодно (лично я это понимаю)
расширение знаний
Reducer
объяснять: Редуктор — это чистая функция, которая принимает старыйstate
а такжеaction
, который возвращает новое состояние
reduce
Этот пример исходит изMDN
const array1 = [1, 2, 3, 4];
const reducer = (total, currentValue) => total + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
сочиняю (не очень хорошо понимаю)
compose
заключается в выполнении ряда задач (функций)
думать
как вывести1 2 3
function f1() { console.log("1"); }
function f2() { console.log("2"); }
function f3() { console.log("3"); }
Первый
f1();f2();f3();
второй
f3(f2(f1()))
третий
function compose(...funcs) {
// funcs 是一个函数列表
if (funcs.length === 0) {
console.log('empty');
} else if (funcs.length === 1) {
return funcs[0];
} else {
return funcs.reduce(
(left, right) => (...args) => {
// console.log(args)
// console.log(right)
// console.log(left)
right(left(...args)) // 相当于 f3(f2(f1()))
}
);
}
}
compose(f1,f2,f3)()
Redux
Шаги для использования
- нужен один
store
хранить данные -
store
внутриreducer
инициализацияstate
и определитьstate
Изменить правила - пройти через
dispatch
Одинaction
внести изменения в данные -
action
Отправитьreducer
В функции по поступающемуaction
изtype
, вернуть новыйstate
пример кода
// 创建store store/ReduxStore
import { createStore } from 'redux';
// 创建数据并指定行为
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'add':
return state + 1
case 'minus':
return state - 1
default:
return state
}
}
// 创建一个store
const store = createStore(counterReducer)
export default store
// ReduxPage
import React, { Component } from 'react';
import store from '../store/ReduxStore';
export default class ReduxPage extends Component {
componentDidMount() { // 挂在之后
store.subscribe(() => { // 执行订阅
console.log('subscribe');
this.forceUpdate(); // 重新render
//this.setState({}); // 这个也可以,内部方法也是forceUpdate
});
}
// 执行行为
add = () => {
// 派发action
store.dispatch({ type: 'add' });
};
minus = () => {
store.dispatch({ type: 'minus' });
};
stayStatic = () => {
store.dispatch({ type: 'others' });
};
render() {
console.log('store', store);
return (
<div>
<h1>ReduxPage</h1>
<p>获取store数据</p>
<p>{ store.getState() }</p>
<button onClick={ this.add }>+</button>
<button onClick={ this.minus }>-</button>
<button onClick={ this.stayStatic }>+-</button>
</div>
);
}
}
Уведомление
Если данные не обновляются после клика. то статус подписки не изменился (т.е. нетrender
)
Обращать внимание
-
createStore
Создайтеstore
-
reducer
Инициализировать и изменить функции состояния -
getState
получить значение статуса -
dispatch
Отправить обновление -
subscribe
Изменить подписку
react-redux
кReact
способ письмаRedux
Предоставляет два API
-
Provider
Предусмотрено для компонентов-потомковstore
(аналогичныйContext
) -
connect
Предоставление данных и методов изменения для компонентов
react-thunk
Оригинал можно выполнять только синхронно, после установки можноАсинхронная работа
react-logger
Может использоваться только в среде разработки. для вывода каждогоaction
store
Вы обнаружите, что есть ещеredux
введение, описаниеreact-redux
также на основеRedux
import { createStore, applyMiddleware,combineReducers } from 'redux';
import logger from 'import React, { Component } from 'react';
import { connect } from 'react-redux';
class ReactReduxPage extends Component {
render() {
const { num, add, minus,asyAdd } = this.props;
// console.log(this.props) // 原本这里面什么都没有,再进行connect映射后出现了
return (
<div>
<h1>ReactReduxPage</h1>
<p>{ num }</p>
<button onClick={ add }>+</button>
<button onClick={ minus }>-</button>
<button onClick={ asyAdd }>asyAdd</button>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state)
return { num: state };
// return { num: state.counter };// 加了combineReducers 之后需要这么做 因为是一个对象
};
const mapDispatchToProps = {
add: () => {
return { type: 'add' };
},
minus: () => {
return { type: 'minus' };
},
asyAdd: ()=> dispatch =>{
setTimeout(()=>{
dispatch ({type: 'add'})
},1000)
}
};
export default connect(
mapStateToProps,//状态映射 mapStateToProps
mapDispatchToProps,//派发事件映射
)(ReactReduxPage)
';
import thunk from 'redux-thunk';
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'add':
return state + 1
case 'minus':
return state - 1
default:
return state
}
}
// const store = createStore(counterReducer)
// const store = createStore(combineReducers({counter: counterReducer}), applyMiddleware(logger, thunk))
const store = createStore(counterReducer, applyMiddleware(logger, thunk))
export default store
applyMiddleware
Предоставляет промежуточное ПО расширения (Есть заказ, это заранее выполняет логгер, и выполняет преобразователь)
combineReducers
Пространства имен, Если у вас несколько магазинов, используйте это для доступа к каждому магазину как к объекту.
ReactReduxPage
import React, { Component } from 'react';
import { connect } from 'react-redux';
class ReactReduxPage extends Component {
render() {
const { num, add, minus,asyAdd } = this.props;
// console.log(this.props) // 原本这里面什么都没有,再进行connect映射后出现了
return (
<div>
<h1>ReactReduxPage</h1>
<p>{ num }</p>
<button onClick={ add }>+</button>
<button onClick={ minus }>-</button>
<button onClick={ asyAdd }>asyAdd</button>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state)
return { num: state };
// return { num: state.counter };// 加了combineReducers 之后需要这么做 因为是一个对象
};
const mapDispatchToProps = {
add: () => {
return { type: 'add' };
},
minus: () => {
return { type: 'minus' };
},
asyAdd: ()=> dispatch =>{
setTimeout(()=>{
dispatch ({type: 'add'})
},1000)
}
};
export default connect(
mapStateToProps,//状态映射 mapStateToProps
mapDispatchToProps,//派发事件映射
)(ReactReduxPage)
Конечно, если вам это слишком не нравится, вы можете взять его и отпустить. Наконец импортируйте его
составной компонент
это тожеОфициальная рекомендацияЧто делать: мы рекомендуем использовать композицию вместо наследования для повторного использования кода между компонентами.
Для студентов, которые изучали Vue, я лично понимаю это как слот, похожий на Vue
// Layout.js
import React, { Component } from 'react';
import TabBar from '../components/TabBar';
class Layout extends Component{
constructor(props) {
super(props);
}
componentDidMount() {
const {title = '首页'} = this.props
document.title = title
}
// 兼容具名和不具名 compatible
compatibleNamed(children) {
let doc = []
if (children.?typeof) {
doc.push(children)
} else {
for (let item in children){
doc.push(children[item])
}
}
return doc
}
render() {
console.log(this.props)
return (
<div>
<h1>我是布局页面</h1>
{/* 不具名的使用*/}
{/*{*/}
{/* this.props.children*/}
{/*}*/}
{/* 这个是具名的使用*/}
{
this.props.children.btn
}
<h2>兼容过后</h2>
{this.compatibleNamed(this.props.children).map((item,index)=>{
return (
<div key={index}>
{item}
</div>
)
})}
{ this.props.showTab && <TabBar/> }
</div>
)
}
}
export default Layout
// Home.js
import React, { Component } from 'react';
import Layout from '../ComponentCombination/Layout';
class Home extends Component {
render() {
return (
<DisNamed/>
)
}
}
function Named(props) {
return (
<Layout showTab={ false } title='商城首页'>
<h1>我是首页</h1>
</Layout>
)
}
function DisNamed() {
return (
<Layout showTab={ false } title='商城首页'>
{ {
btn: <button>我是按钮</button>
} }
</Layout>
)
}
export default Home
// TabBar.js
import React, { Component } from 'react';
class TabBar extends Component{
render() {
return (
<div>
<h1>我是TabBar</h1>
</div>
)
}
}
export default TabBar
Анонимный
Приведенный выше пример легко запутать, напишите проще
import React, { Component } from 'react';
import TabBar from '../components/TabBar';
class Layout extends Component{
constructor(props) {
super(props);
}
render() {
console.log(this.props)
return (
<div>
<h1>我是布局页面</h1>
{/* 不具名的使用*/}
{
this.props.children
}
{ this.props.showTab && <TabBar/> }
</div>
)
}
}
export default Layout
this.props.children
показаны дваLayout
содержание между
Например
<Layout>
<h1>hello</h1>
</Layout>
this.props.children
показывает, что<h1>hello</h1>
по имени
//Layout
import React, { Component } from 'react';
import TabBar from '../components/TabBar';
class Layout extends Component{
constructor(props) {
super(props);
}
render() {
console.log(this.props)
return (
<div>
<h1>我是布局页面</h1>
{/* 这个是具名的使用*/}
{
this.props.children.btn
}
{ this.props.showTab && <TabBar/> }
</div>
)
}
}
export default Layout
// Home.js
import React, { Component } from 'react';
import Layout from '../ComponentCombination/Layout';
class Home extends Component {
render() {
return (
<Named/>
)
}
}
function DisNamed() {
return (
<Layout showTab={ false } title='首页'>
{ {
btn: <button>我是按钮</button>
} }
</Layout>
)
}
export default Home
При передаче именованных компонентов вам нужно использовать double{{}}
пакет. Просто используйте его как свойство
Отличия и совместимость
по имениа такжеАнонимныйОтличие в том, что при переходеthis.props.children
Если они содержат?typeof
Атрибуты.如果不含有,则是具名。反之,含有则是不具名
Как это совместимо?
Знайте разницу между ними. начни с разницы
compatibleNamed(children) {
let doc = []
if (children.?typeof) { // 如果不是具名 直接放到数组里
doc.push(children)
} else {
for (let item in children){ // 如果是具名,则便利children。把里面的内容放进数组里
doc.push(children[item])
}
}
return doc // 返回的是一个数组
}
// 使用
render(){
return (
{this.compatibleNamed(this.props.children).map((item,index)=>{
return (
<div key={index}>
{item}
</div>
)
})}
}
Функции высшего порядка (каррирование)
Компонент высшего порядка — это фабричная функция, которая принимает один компонент и возвращает другой.
Простой пример
function Child() {
return (
<div>
child
</div>
)
}
// Cmp是组件 props 是组件的props
const foo = Cmp => props =>{
return (
<div style={{boder: '1px solid #ccc'}}>
<Cmp {...props}/>
</div>
)
}
function APP() {
const Foo = foo(Child)
return (
<div>
<Foo/>
</div>
)
}
Пример изменения предыдущего контекста
Создание компонентов более высокого порядка
import { Consumer } from './Context';
import React from 'react';
//Cum 是组件 props是组件传递的props
const HOC = Cum => props => {
return (
<Consumer>
{ value => <Cum { ...props } { ...value }/> }
</Consumer>
)
}
export default HOC
Оригинальное написание контекста
import React, { Component } from 'react';
import ContextChild2 from './child2';
import HOC from './Hoc Hooks';
import {Consumer} from './Context';
class ContextChild extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>我是第一层</h1>
<h2>{this.props.name}</h2>
<Consumer>
{
value => <ContextChild2 {...value}/>
}
</Consumer>
</div>
)
}
}
export default ContextChild
После переключения на HOC (компоненты более высокого порядка)
// child
import React, { Component } from 'react';
import ContextChild2 from './child2';
import HOC from './Hoc Hooks';
class ContextChild extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>我是第一层</h1>
<h2>{this.props.name}</h2>
{/*直接调用*/}
<ContextChild2/>
</div>
)
}
}
export default HOC(ContextChild)
// child2.js
import React, { Component } from 'react';
import HOC from './Hoc Hooks';
class ContextChild2 extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props)
console.log('我是第二层的')
return (
<div>
<h1>我是第二层的</h1>
<h1>{this.props.name}</h1>
</div>
);
}
}
export default HOC(ContextChild2)
изначально нужно было написатьConsumer
Теперь для ее решения нужна только одна функция. Очень удобно
объяснить подробно
Может быть, все еще немного запутался. Я собираюсь сказать это подробно
import { Consumer } from './Context';
import React from 'react';
const HOC = Cum => props => {
return (
<Consumer>
{ value => <Cum { ...props } { ...value }/> }
</Consumer>
)
}
export default HOC
Cum
является компонентом,props
параметр, переносимый компонентом,value
даprovider
распределенные данные.
Сравните следующее.Он принимает компонент в качестве параметра и возвращает другой новый компонент, передавая компонент.
<Consumer>
{
value => <ContextChild {...value}/>
}
</Consumer>
полностью пересмотрено
// ContextFather.js
import React from 'react';
import ContextChild from './child';
import {Consumer,Provider} from './Context';
const data = {
name: '我是father数据',
}
function ContextApp() {
return (
<div>
<Provider value={ data }>
{/*<Consumer>*/}
{/* {*/}
{/* value => <ContextChild {...value}/>*/}
{/* }*/}
{/*</Consumer>*/}
<ContextChild/>
</Provider>
</div>
)
}
export default ContextApp
// child.js
import React, { Component } from 'react';
import ContextChild2 from './child2';
import HOC from './Hoc Hooks';
class ContextChild extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>我是第一层</h1>
<h2>{this.props.name}</h2>
<ContextChild2/>
</div>
)
}
}
export default HOC(ContextChild)
// child2.js
import React, { Component } from 'react';
import HOC from './Hoc Hooks';
class ContextChild2 extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props)
console.log('我是第二层的')
return (
<div>
<h1>我是第二层的</h1>
<h1>{this.props.name}</h1>
</div>
);
}
}
export default HOC(ContextChild2)
маршрутизация
Установить
react-router содержит 3 библиотеки: react-router, react-router-dom и react-router-native.
react-router предоставляет самые основные функции маршрутизации.При реальном использовании мы не будем устанавливать react-router напрямую, а выберем установку в соответствии со средой, в которой работает приложение.
react-router-dom (используется в браузерах)
react-router-native (используется в rn).
Реакция-роутер-дом и реакция-роутер-роднойОба полагаются на реактивный маршрутизатор, поэтому при установке react-router также будет установлен автоматически
npm install --save react-router-dom
основное использование
Обычно используемый BrowserRouter, Link-Link, Route-Route, Exclusive Route Switch, Redirect Route-Redirect
// RouterPage
import React, { Component } from 'react';
import { BrowserRouter, Link, Route,Switch } from 'react-router-dom';
import HomePage from './HomePage';
import UserPage from './UserPage';
import SearchPage from './Search';
import Login from './Login';
export default class RouterPage extends Component {
render() {
return (
<div>
<h1>RouterPage</h1>
<BrowserRouter>
<nav>
<Link to="/">首页 </Link>
<Link to="/user">用户中心 </Link>
<Link to="/login">登陆 </Link>
</nav>
{/* 根路路由要添加exact,实现精确匹配 不加这个可能会出现多次渲染 */ }
<Switch>
{/*匹配到之后就不在继续往下匹配 Switch作用*/}
<Route exact path="/" component={ HomePage }/>
{/*<Route path="/user" component={ UserPage }/>*/}
<Route path="/login" component={ Login }/>
{/*404 页面 一定要放到最后*/}
<Route component={() => <h1>404</h1>} />
</Switch>
</BrowserRouter>
</div>
);
}
}
динамическая маршрутизация
// RouterPage
<Link to="/search/123">详情 </Link>
<Route path="/search/:id" component={SearchPage} />
// Search
import React from 'react';
function SearchPage(props) {
console.log(props) // 有三个 match history location
return(
<div>
<p>通过这样获取到传递的参数</p>
SearchPage{props.match.params.id}
</div>
)
}
export default SearchPage
Защита маршрутизации (выделено)
Случай: Например, вам нужно авторизоваться, чтобы войти на страницу пользователя, затем вам нужно использовать роутинг гуард.
Студенты, знакомые с Vue, должны знать, что я хочу выразить.
//RouterPage
...
import RouterGuard from './RoutingGuard';
...
<Link to="/user">用户中心 </Link>
<Link to="/login">登陆 </Link>
<RouterGuard path ='/user' isLogin={true} Cmp={UserPage}/>
...
RouterGuard (выделено)
// RouterGuard
import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
function RouterGuard(props) {
const { Cmp, path, isLogin, ...rest } = props
console.log(rest) // 一些剩下的参数
return (
<Route
{ ...rest }
render={ prop =>{
console.log(prop) // 一些路由的参数
return isLogin ? (<Cmp { ...prop } />) :
(<Redirect to={ { pathname: '/login', redirect:props.location.pathname} }/>
)} }
>
</Route>
)
}
export default connect(
state => {
return {
isLogin: state.user.isLogin,
}
},
)(RouterGuard)
Это в основном используется для функций рендерингаrender
, и тернарное выражение
другой код страницы
// LoginStore 记得用provider 包裹APP
import { createStore, applyMiddleware, combineReducers } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
const initialLogin = { isLogin: false, name: null };
const userInfo = (state = {...initialLogin}, action) => {
switch (action.type) {
case 'getUser':
return { ...state,isLogin: false }
case 'LoginSuccess':
return { ...state,isLogin: true,name: '阿琛' }
case 'LoginFail':
return { ...initialLogin }
default:
return state
}
}
const store = createStore(
combineReducers({
user: userInfo
}),
applyMiddleware(logger, thunk))
export default store
// Login
import React from 'react';
import { connect } from 'react-redux';
import {Redirect} from 'react-router-dom'
function Login(props) {
const {location,login,isLogin} = props
console.log(props)
const redirect = location.redirect?location.redirect:'/'
if (isLogin){
return <Redirect to={redirect} />;
}
return (
<div>
<h1>LoginPage</h1>
<button onClick={login}>登陆</button>
</div>
)
}
export default connect(state => ({
isLogin: state.user.isLogin,
}), {
login: () => {
return { type: 'LoginSuccess' };
},
},
)(Login)
Семейное ведро React
Это было представлено много раньше, а затем я представлю некоторые вещи, которые не были представлены.
redux-saga
Redux-saga — промежуточное ПО Redux, в основном занимается асинхронной обработкой в архитектуре React, определяется как генератор (ES6) и работает в виде мониторинга.
Эффект аналогичен реакции-thunk. все для решенияасинхронный
базовые знания
Generato
Функция генератора — это решение для асинхронного программирования, предоставляемое ES6. Его синтаксис и поведение полностью отличаются от традиционных функций. Подробности см. в справочнике.Учитель Жуань Ифэн объяснил
Установить
npm install --save redux-saga
кейс
Также является функцией входа
// RouterPage 加上一个saga 登陆
...
<Link to="/login2">Saga登陆 </Link>
<Route path="/login2" component={ SagaLogin }/>
...
//SageLogin
import React,{useState} from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom'
// 这里用的是Hooks 写起来顺手 直接用这个了
function SagaLogin(props) {
let [name,setName] = useState('')
const { location, login, isLogin, loading,error } = props
console.log(props)
const redirect = location.redirect ? location.redirect : '/'
if (isLogin) {
return <Redirect to={ redirect }/>;
}
return (
<div>
<h1>LoginPage</h1>
<p>{name}</p>
{error && <p>{error}</p>}
<input type="text" onChange={(e)=>{setName(e.target.value)}}/>
<button onClick={ () => login(name) }>
{ loading ? '登录中...' : '登录' }
</button>
</div>
)
}
// 这里面的就不再解释了 再说react-thunk 解释过
export default connect(state => (
{
isLogin: state.user.isLogin,
loading: state.user.loading,
error: state.user.error,
}), {
// 这里调用Saga的login 函数,因为有事件监听。触发了loginHandle
login: name => ({ type: 'login', name }),
},
)(SagaLogin)
фокус
//Saga 这里是把逻辑分离了出来,只是为了更好维护
import { call, put, takeEvery } from 'redux-saga/effects';
// 模拟登录接口
const UserService = {
login(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (name === '阿琛') {
resolve({ name: '阿琛' });
} else {
reject('用户名或密码错误');
}
}, 1000);
});
},
};
// worker saga
function* loginHandle(action) {
console.log('loginHandle');
try {
// 派发清求 到ReduxSage 使得loading为true
yield put({ type: 'requestLogin' });
// 执行回调函数。获取结果
const res = yield call(UserService.login, action.name);
// 把结果派发回去
yield put({ type: 'requestSuccess', res });
} catch (err) {
yield put({ type: 'requestFailure', err });
}
}
// watcher saga 事件监听 监听login事件
function* mySaga() {
yield takeEvery('login', loginHandle);
}
export default mySaga;
// ReduxSage
import { createStore, combineReducers, applyMiddleware } from 'redux';
import mySaga from './Saga';
import createSagaMiddleware from "redux-saga";
// 创建一个中间件
const sagaMiddleware = createSagaMiddleware();
// 初始化状态
const initialLogin = { isLogin: false, loading: false, name: '', error: '' };
// 定义Reducer
// 注意 必须又default 从action 里面拿到传递过来的参数
function loginReducer(state = { ...initialLogin }, action) {
switch (action.type) {
case 'requestLogin':
return { ...initialLogin, loading: true };
case 'requestSuccess':
return { ...state, isLogin: true, loading: false,name: action.name };
case 'requestFailure':
console.log(action)
return {...state,error: action.err}
default:
return state;
}
}
const store = createStore(combineReducers(
{ user: loginReducer }
),
// applyMiddleware(thunk)
applyMiddleware(sagaMiddleware)
);
// 记得运行一下
sagaMiddleware.run(mySaga)
// 如果按看上面的例子了话,记得换一下store 再index.js 里用provider 来传递store
export default store;
Следующие не изучены и не разобраны, и я поспешу разобраться с ними в ближайшие дни
Dva
Umi
mobx
Жизненный цикл (еще не организован)
React HOOKs
То, что было изучено, в основном обновляется, и могут быть некоторые недостающие места. Пожалуйста, также укажите
Хуки появились в React 16.8 впервые. Это позволяет вам использовать состояние и другие функции React без написания классов.
Решать проблему
- Сложно повторно использовать логику состояния между компонентами. Он может использовать реквизиты рендеринга и компоненты более высокого порядка. React должен предоставить лучший собственный способ совместного использования логики состояния. Хук позволяет повторно использовать логику состояния без изменения структуры компонента.
- Сложные компоненты становятся трудными для понимания, хуки разбивают взаимосвязанные части компонента на более мелкие функции (например, настройка подписок или запрос данных).
- неуловимые классы, в том числе неуловимые
this
Меры предосторожности
- Хуки можно вызывать только на самом внешнем уровне функции.Не вызывайте его в циклах, условных выражениях или подфункциях.
- Хуки можно вызывать только в функциональных компонентах React.. Не вызывать другие функции JavaScript
useState
- useState возвращает пару значений: текущее состояние и его функцию, которую вы можете вызвать в обработчике событий или в другом месте. Это похоже на this.setState компонентов класса,Но это не объединяет новое состояние со старым состоянием
- Единственным параметром useState является начальное состояние.
Простой пример счетчика
import React,{useState} from 'react';
function Counter() {
// useState会返回两个,一个是当前值。一个是改变它的函数
let [state,setState] = useState({num: 0})
let [num,setNum] = useState(0)
// 两种方式 看个人喜好
return (
<div>
<p>{state.num}</p>
<p>{num}</p>
<button onClick={()=>{setState({num: state.num +1})}}>+</button>
<button onClick={()=>{setNum(num + 1)}}>+</button>
</div>
)
}
Каждый рендер — это отдельное замыкание
- Каждый рендер имеет свои реквизиты и состояние.
- Каждый рендер имеет свой обработчик событий.
- Предупреждение «захватывает» состояние, когда я нажимаю кнопку.
// alter 捕获得是点击时候得状态
function Counter2 () {
// useState 返回两个,一个是当前状态的属性,一个是修改状态的方法。
let [state, setState] = useState({num: 0})
let changeLater = ()=>{ // 这个alter 会捕获当前得状态。即点击按钮时候得状态
setTimeout(()=>{
alert(state.num)
}, 2000)
}
return (
<div>
<p>{ state.num }</p>
<button onClick={ () => setState({num: state.num+1}) }>+</button>
<button onClick={ changeLater }>changeLater</button>
</div>
)
}
Функциональное обновление (решение приведенного выше примера)
// 改变状态注意
function Counter3 () {
// useState 返回两个,一个是当前状态的属性,一个是修改状态的方法。
let [state, setState] = useState({num: 0})
let lazyAdd = ()=>{ // 这个alter 会捕获 当前得状态。即点击按钮时候得状态
setTimeout(()=>{
setState({num: state.num+1})
}, 2000)
}
let lazyAdd2 = ()=>{ // 这个alter 会捕获之后的状态。
setTimeout(()=>{
setState((state) => ({num: state.num+1}))
}, 1000)
}
return (
<div>
<p>{ state.num }</p>
<button onClick={ () => setState({num: state.num+1}) }>+</button>
<button onClick={ lazyAdd }>lazyAdd</button>
<p>如果这样写得话,会获取到点击时候得状态在进行改变</p>
<button onClick={ lazyAdd2 }>lazyAdd2</button>
<p>这样写,改变了整个state,就可以正常相加了</p>
</div>
)
}
Если новое состояние необходимо вычислить, используя предыдущее состояние, то в setState можно передать функцию. Функция примет предыдущее состояние и вернет обновленное значение.
ленивое состояние
- Параметр initialState будет работать только при начальном рендеринге компонента и будет игнорироваться при последующих рендерингах.
- Если начальное состояние нужно получить сложным вычислением, вы можете передать функцию, вычислить и вернуть начальное состояние в функцию, эта функция вызывается только во время начального рендеринга.
// 惰性state
// initialState 初始状态参数只会在组件初始渲染得时候调用,后续被忽略
// 且不会自己整合state,需要自己整合
function Counter4 () {
let [state, setState] = useState(function () {
console.log(' 初始化')
return {num: 0, name:'计算器'}
})
return (
<div>
<p>{state.num}</p>
<button onClick={ () => setState({num: state.num+1}) }>+</button>
<p>只会输出一次log</p>
<p>React.StrictMode 模式下会输出两次log</p>
<p>这是展示他不会整合state</p>
<p>{ state.name }:{state.num}</p>
<p>如果点击上面得button,则name会消失,下面这个则不会</p>
<button onClick={ () => setState({...state, num: state.num+1}) }>+</button>
</div>
)
}
Причина, почему пропадает калькулятор после нажатия первой кнопки: setState
не интегрируется в себяState
, нам нужно вручную интегрировать его самостоятельно
оптимизация производительности
Object.is
MDN,использоватьObject.is
Сравнивать.
function Counter5 () {
let [state, setState] = useState(function () {
return {num: 0, name:'计算器'}
})
console.log('当state发生改变得时候,会重新得渲染这个函数,则会继续得输出')
console.log('Render5 ')
return (
<div>
<p>{state.num}</p>
<button onClick={ () => setState({...state, num: state.num+1}) }>输出+</button>
<p>点击这个不会输出 Render5</p>
<button onClick={ () => setState(state) }>不输出+</button>
<p>原因,内部当发现这个state没有发生改变得时候,将不再会渲染这个</p>
</div>
)
}
Кешировать CallBack
функция кэширования
let lastClick;
let lastClick2;
function Counter7(callback, deps) {
let [state, setState] = useState({num: 0})
const contralAdd = ()=> setState({num: state.num+1})
console.log(lastClick === contralAdd)
lastClick = contralAdd // 一直输出false 证明不是同一个函数
/**********************************************************************************/
const contralAdd2 = useCallback(
()=> setState({num: state.num+1}), []
// 这个数组称之为依赖数组,里面放属性,属性发生变化,他就重新渲染
// 如果不写 state.num 那点击下面得按钮只会变化一次。
// 如果写了得话,则会每次改变都会重新渲染
)
console.log(lastClick2 === contralAdd2)
lastClick2 = contralAdd2 // 一直输出true 证明是同一个函数
return (
<div>
<p>{state.num}</p>
<button onClick={ contralAdd }>+</button>
<p>这样得话每次会生成一个新得函数,性能会变低</p>
<button onClick={ contralAdd2 }>+</button>
<p>这样得话更具log显示,得出是一个函数,提高性能</p>
</div>
)
}
три исхода
- Если вы нажмете знак «плюс» над выводом журнала
lastClick === contralAdd false
lastClick2 === contralAdd2 true
Причина: изменен номер состояния., который повторно отображает Counter. такПервыйвывод ложный.Во-вторых, потому что добавлен useCallback, в массив зависимостей не добавляется атрибут, поэтому программа считает, что он не изменился, поэтому вывод верен
-
Если вы нажмете знак + под выводом журнала
lastClick === contralAdd false lastClick2 === contralAdd2 true
Он также выведет это, причина, если вы не напишете свойство зависимости, затем нажмите кнопку нижеизменить только один раз. И значение изменилось. Итак, первое верно, а второе ложно, и снова нажмите кнопкуне изменится
(Это немного неточно, но на самом деле изменилось, потому чтоПричина закрытия, в результате чего вторая функция
state.num
Он всегда указывает на инициализированный 0, поэтому он не изменился) -
Добавить ксвойство зависимостиПозже. выходной результат
lastClick === contralAdd false lastClick2 === contralAdd2 false
Излишне говорить, что свойства зависимостей изменились. заставляет обе функции повторно отображать
memo
Чистая функция, оптимизация или кеш,компонент кэша
import React,{memo,useMemo,useState} from 'react';
function Child() {
console.log('我是Child')
return (
<h2>我是child</h2>
)
}
function Child2(props) {
console.log('render child2')
console.log(props)
return (
<p>我是Child2{props.name}</p>
)
}
Child = memo(Child)
Child2 = memo(Child2)
function MemoApp() {
let [state,setState] = useState({num: 0})
let [name, setName] = useState('测试')
return(
<div>
<p>{state.num}</p>
<button onClick={()=>{setState({num: state.num +1 })}}>+</button>
<br/>
<input type="text" value={name} onChange={(e)=>{setName(e.target.value)}}/>
<Child/>
<Child2 name={name}/>
</div>
)
}
export { MemoApp }
если не добавленоmemo
, затем просто нажмите знак +,Child
Произойдет рендеринг.
популярное объяснение: Пока последний результат рендеринга такой же, как на этот раз. не будет рендериться, иначе рендерится
useMemo
Также используется для кэширования.кешировать данные
let lastData
function UseMemoApp() {
let [state, setState] = useState(0)
let [name, setName] = useState('测试')
const data = state
const data2 = useMemo(() => (state), []);
console.log('data===oldData ', data === lastData);
const addClick = useCallback(() => setState(state + 1), [state])
lastData = data;
return(
<div>
<p>{state}</p>
<p>{data2.state}</p>
<button onClick={addClick}>+</button>
<p>改变input的值,输出true 证明数据做了缓存</p>
<input type="text" value={name} onChange={(e)=>{setName(e.target.value)}}/>
</div>
)
}
тот, что вышеuserCallback
Можете не обращать внимания, я только что сделал кеш. Сосредоточьтесь на двухdata
измененныйinput
После значения внутри журнал выводит true, что доказывает, что данные кэшируются успешно, данные изменяются, и выводится false.
useReducer
- Альтернатива useState. Он принимает редюсер вида (состояние, действие) => newState и возвращает текущее состояние и его метод отправки.
- В некоторых сценариях useReducer больше подходит, чем useState, например, логика состояния более сложная и содержит несколько подзначений, или следующее состояние зависит от предыдущего состояния и т. д.
пример, счетчик
import React, { useReducer } from 'react';
// 初始化状态
const initialState = 0;
// 初始一个reducer state 状态 action 派发状态
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { number: state.num + 1 };
case 'decrement':
return { number: state.num - 1 };
default:
throw new Error();
}
}
// 把数据变成 对象
function init(initialState) {
return { num: initialState }
}
function ReducerCounter() {
const [state, dispatch] = useReducer(reducer, initialState, init);
return (
<>
Count: { state.num }
<p></p>
<button onClick={ () => dispatch({ type: 'increment' }) }>+</button>
<button onClick={ () => dispatch({ type: 'decrement' }) }>-</button>
</>
)
}
export { ReducerCounter }
Напишите useState с помощью useReducer
пользовательские крючки
если имя функцииначинается с использования,И позвони другому Крюку, это называется пользовательским хуком.
Вот лишь краткое объяснение. Подробно он будет представлен позже
function useStateSelf(initState) {
function init(initialState){
return {number:initialState};
}
// userCallback 做缓存
const reducer = useCallback((state,action) => {
console.log(action)// 看log 输出判断 返回值
console.log(state)
return action.payload // 加{} 需要 写 action.payload
// return payload// 不加{}直接写action
})
let [state,dispatch] = useReducer(reducer, initState,init)
console.log(dispatch)
function setState(payload) {
console.log(payload) // 整个对象 {number : x}
dispatch({payload})
// dispatch(payload)
}
return [state,setState]
}
// 调用
const [state, setState] = useStateSelf(initialState);
return(
<div>
<p>自定义hooks</p>
<p>{state.number}</p>
<button onClick={() => setState({number: state.number + 1 })}>+</button>
<button onClick={() => setState({number: state.number - 1 })}>-</button>
</div>
)
Анализ мыслей, сначала обратитесь кuseState
При использовании вам нужно передать состояние (в этом примере рассматривается только значение, а не объект). возвращает массив,состояние и функции, меняющие состояние.
useEffect
Effect Hook
позволяет выполнять в функциональных компонентахпобочный эффект операции(Извлечение данных, настройка подписок и ручное изменение DOM в компонентах React — все это побочные эффекты)
Это то же самое, что и в компоненте классаcomponentDidMount
,componentDidUpdate
а такжеcomponentWillUnmount
Имеет ту же цель, просто объединена в один API
С помощью useEffect вы можете сказать компоненту React, что ему нужно что-то сделать после рендеринга?
useEffect будет выполняться после каждого рендераПо умолчанию это после первого рендера.а такжеВыполняется после каждого обновления
Объяснение побочных эффектов
Побочные эффекты делятся на требующие устранения и не требующие устранения.
- Например, отправка сетевых запросов, ручное изменение DOM, ведение журнала.Нет необходимости очищатьоперация
- необходимо очистить, т.е.Подписка на внешние источники данных,таймер и т. д.. В этом случае работа по очистке очень важна для предотвращения утечек памяти!
образец кода
//useEffect.js 这样就完成了点击更改页面标题的功能 无需清除
import React, { useEffect, useState } from 'react';
function CounterEffect() {
let [state, setState] = useState(0)
useEffect(()=>{
document.title = state
})
return (
<div>
<p>{state}</p>
<button onClick={()=> setState(state+1)}>+</button>
</div>
)
}
export {CounterEffect}
// 返回一个清理函数
function CounterEffect3() {
let [state, setState] = useState(0)
useEffect(()=>{
let time = setTimeout(()=>{
setState(state +1 )
},1000)
return ()=>{
console.log('清除')
clearTimeout(time)
}
})
return (
<div>
<p>{state}</p>
<button onClick={()=> setState(state+1)}>+</button>
</div>
)
}
React очистится, когда компонент будет размонтирован
Пропустите это, чтобы начать оптимизацию производительности.
// 跳过这个开始性能优化 做缓存
function CounterEffect3() {
let [state, setState] = useState(0)
// 这个函数会在组件更新或者挂载的时候 执行
// 第二个称之为 依赖数组。跟useCallback等作用差不多
// 如果没有给定依赖项,则仅初始调用一次,不会再调用
useEffect(()=>{
console.log('useEffect调用')
// document.title = state
}, []) // 添加依赖项就会调用 [state]
return (
<div>
<p>{state}</p>
<button onClick={()=> setState(state+1)}>+</button>
</div>
)
}
useRef
// useRef 返回一个可变的 ref 对象 通过ref来访问dom
let last;
let last2;
function Child() {
// 以前的做法
let ref = React.createRef()
console.log('last === ref',last === ref) // false 证明每次都不是一个
last = ref
// 使用hooks之后
let ref2 = useRef()
console.log('last2 === ref2',last2 === ref2) // true 证明是同一个
last2 = ref2
function getFocus() {
ref.current.focus();
}
function getFocus2() {
// `current` 指向已挂载到 DOM 上的文本输入元素
ref2.current.focus();
}
return (
<div>
<input type="text" ref={ref} />
<button onClick={getFocus}>获取焦点</button>
<br/>
<input type="text" ref={ref2} />
<button onClick={getFocus2}>获取焦点2</button>
</div>
)
}
function CounterRef() {
let [state, setState] = useState(0)
return (
<div>
<Child></Child>
<button onClick={ () => setState(state + 1) }>+</button>
</div>
)
}
Отсюда видно, чтоuseref
также кэш
useImperativeHandle
Необходимые знания: forwardRef
Если вы хотите увеличить атрибут ref функционального компонента, то метод должен быть обернут с помощью forwardRef, также известного каквперед исх
недостаток:
- Использование этого метода очень опасно. Потому что это нарушает принцип инкапсуляции.
- Родительский компонент может напрямую управлять домом дочернего компонента.
function Child2(props, ref) { // 不能不写这个props
return (
<div>
<input type="text" ref={ref} />
</div>
)
}
let ChildFor = forwardRef(Child2)
function CounterRef2() {
let ref = useRef()
function getFocus() {
ref.current.focus();
// ref.current.value = '123' // 直接可以操作
}
let [state, setState] = useState(0)
return (
<div>
<p>如果想给一个函数组件增加ref 属性,则需要通过forwardRef方法进行包裹</p>
<ChildFor ref={ref}/>
<button onClick={ () => setState(state + 1) }>+</button>
<button onClick={getFocus}>获取焦点</button>
</div>
)
}
useImperativeHandle
Позволяет настроить значение экземпляра, предоставляемое родительскому компоненту при использовании ref
function Child3(props, ref) { // 不能不写这个props
let refObject = useRef()
// 定义了两个方法给ref 父组件操作的实际上是这两个方法
useImperativeHandle(ref, ()=>({
focus(){
refObject.current.focus()
},
setVal(val){
refObject.current.value = val
}
}))
return (
<div>
<input type="text" ref={refObject} />
</div>
)
}
let ChildFor2 = forwardRef(Child3)
function CounterRef3() {
let ref = useRef() // 得到的是 useImperativeHandle 返回的对象
console.log(ref) // 里面有两个方法,即在child3里面定义的方法
function getFocus() {
ref.current.focus();
ref.current.value = '123' // 即使写上也不会有改变
}
function setVal(val) {
ref.current.setVal(val);
}
return (
<div>
<ChildFor2 ref={ref}/>
<button onClick={getFocus}>获取焦点</button>
<button onClick={()=>setVal(123456)}>设置值</button>
</div>
)
}
Пользовательские крючки
- Функция, имя которой начинается с use и вызывает другие хуки, называется пользовательским хуком.
- это способ повторного использования логики состояния, он не использует повторно само состояние
- Каждый вызов имеет полностью независимое состояние
import React, { useState, useEffect } from 'react';
function useBaseCounter() {
// 函数的名字以 use 开头,并且调用了其他的 Hook,则就称其为一个自定义 Hook
const [state,setState] = useState(0)
// 是一种复用状态逻辑的方式,它不复用 state 本身
// 每次调用都有一个完全独立的 state
useEffect(() => {
console.log('开启一个新的定时器')
const $timer = setInterval(()=>{
setState(state+1);
},1000);
return ()=>{
console.log('销毁老的定时器')
clearInterval($timer);
}
});
return state;
}
function CounterTest() {
let num = useBaseCounter()
return (
<>
<p>{num}</p>
</>
)
}
function CounterTest2() {
let num = useBaseCounter()
return (
<>
<p>{num}</p>
</>
)
}
export {CounterTest,CounterTest2}