关于react.js:React-useReducer-终极使用教程

6次阅读

共计 15064 个字符,预计需要花费 38 分钟才能阅读完成。

本文完整版:《React useReducer 终极应用教程》

useReducer 是在 react V 16.8 推出的钩子函数,从用法层面来说是能够代替 useState。置信后期应用过 React 的前端同学,大都会经验从 class 语法向 hooks 用法的转变,react 的 hooks 编程给咱们带来了丝滑的函数式编程体验,同时很多前端驰名的文章也讲述了 hooks 带来的前端心智的转变,这里就不再着重强调,本文则是聚焦于 useReducer 这个钩子函数的原理和用法,笔者率领大家再一次深刻意识 useReducer。

家喻户晓,useState 罕用在单个组件中进行状态治理,然而遇到状态全局治理的时候,useState 显然不能满足咱们的需要,这个时候大多数的做法是利用第三方的状态管理工具,像 redux,Recoil 或者 Mobx,在代码里就会有

import XXX from Mobx;
import XXX from Redux;
// or
import XXX from Recoil;

这些三方的 import 语句。弱小的 React 团队难道就不能自己实现一个全局的状态治理的 hook 吗,这不,useReducer 为了解决这个需要应运而生。尽管有了 useReducer,然而黄金法令仍旧成立:组件的状态交给组件治理,redux 负责工程的状态治理。本文则负责解说 useReducer 是如何执行全局的状态治理,并且什么时候用适合,什么时候不适合,这里也会提及。

另外如果你正在搭建后盾管理系统,又不想解决前端问题,举荐应用卡拉云,卡拉云是新一代低代码开发工具,可一键接入常见数据库及 API,无需懂前端,仅需拖拽即可疾速搭建属于你本人的后盾管理工具,一周工作量缩减至一天,详见本文文末。

useReducer 工作原理

在学习一个新个性的时候,最好的形式之一是首先相熟该个性的原理,进而能够促成咱们的学习。useReducer 钩子用来存储和更新状态,有点相似 useState 钩子。在用法上,它接管一个 reducer 函数作为第一个参数,第二个参数是初始化的 state。useReducer 最终返回一个存储有以后状态值的数组和一个 dispatch 函数,该 dispatch 函数执行触发 action,带来状态的变动。这其实有点像 redux,不过还是有一些不同,前面笔者会列举这两个概念和不同。

对于 reducer 函数

通常的,reduce 办法在数组的每一个元素上都执行 reducer 函数,并返回一个新的 value,reduce 办法接管一个 reducer 函数,reducer 函数自身会接管 4 个参数。上面这段代码片段揭示一个 reducer 是如何运行的:

const reducer = (accumulator, currentValue) => accumulator + currentValue;
[2, 4, 6, 8].reduce(reducer)
// expected output: 20

在 React 中,useReducer 接管一个返回单组值的 reducer 函数,就像上面这样:

const [count, dispatch] = useReducer(reducer, initialState);

后面提到过,这里的 reducer 函数自身会承受两个参数,第一个是 state,第二个是 action,这个 action 会被 dispatch 执行,就像是:

function reducer(state, action) { }

dispatch({type: 'increment'})

依据不同的 action,reducer 函数会带来不同的 state 的变动,就像是 type 是 increment 的状况,reducer 函数会使得 state 加 1。

懈怠创立初始 state

在编程概念中,懒初始化是提早创建对象的一种伎俩,相似于直到被须要的第一工夫才去创立,还有其余的动作比方值的计算或者昂扬的计算开销。正如下面提到的,useReducer 的第三个参数是一个可选值,可选的懒创立 state 的函数,上面的这段代码是更新 state 的函数:

const initFunc = (initialCount) => {if (initialCount !== 0) {initialCount=+0}
  return {count: initialCount};
}

// wherever our useReducer is located
const [state, dispatch] = useReducer(reducer, initialCount, initFunc);

当 initialCount 变量不为 0 的时候,赋值为 0;并返回 count 的赋值对象。留神第三个参数是一个函数,并不是一个对象或者数组,函数中能够返回对象。

dispatch 函数

dispatch 函数是触发不同 action 的函数,通常的它是承受含有 type 的一个对象,并依据这个 type 来执行对应的 action,action 执行实现之后,render 函数持续发挥作用,这时候会更新 state。当咱们关注的焦点不在 useReducer 用法细节上时,咱们会在宏观上看到 render 和 state 的变动过程。组件触发的 action 都是接管含有 type 和 payload 的对象,其中 type 代表不同 action 的区别,payload 是 action 将要增加到 state 的数据。在应用上,dispatch 用起来十分的简略,就拿 JSX 语法来讲,能够间接在组件事件上触发 action 操作,代码如下:

// creating our reducer function
function reducer(state, action) {switch (action.type) {
   // ...
      case 'reset':
          return {count: action.payload};
    default:
      throw new Error();}
}

// wherever our useReducer is located
const [state, dispatch] = useReducer(reducer, initialCount, initFunc);

// Updating the state with the dispatch functon on button click
<button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button>

留神到,reducer 函数接管 payload 作为传参,其中这个 payload 是来自 dispatch 的奉献,初始化的 state 也是会影响 payload 的。组件之间,应用 props 传递数据的时候,其实 dispatch 也是间接能够封装在函数中,这样不便的从父组件将 dispatch 传递到子组件,就像上面这样:

<Increment count={state.count} handleIncrement={() => dispatch({type: 'increment'})}/>

在子组件中,接管 props,触发的时候,则有:

<button onClick={handleIncrement}>Increment</button>

不触发 dispatch

如果 useReducer 返回的值和以后的一样,React 不会更新组件,也不会引起 effect 的变动,因为 React 外部应用了 Object.is 的语法。

useState 和 useReducer 比拟和区别及利用场景

置信浏览 React 官网文档学习的同学,第一个接触的 Hook 就是 useState,useState 是一个根底的治理 state 变动的钩子,对于更简单的 state 治理,甚至全局的 state 治理,useReducer 是用来干这件事件的。然而,useState 其实是应用到 useReducer 的,这意味着,只有是应用 useState 实现的,都能够应用 useReducer 去实现。然而呢,这两个钩子 useReducer 和 useState 还是有不同的,在用 useReducer 的时候,能够防止通过组件的不同级别传递回调。useReducer 提供 dispatch 在各个组件之间进行传递,这种形式进步了组件的性能。然而,这并不意味着每一次的渲染都会触发 useState 函数,当在我的项目中有简单的 state 的时候,这时候就不能用独自的 setter 函数进行状态的更新,相同的你须要写一个简单的函数来实现这种状态的更新。因而举荐应用 useReducer,它返回一个在从新渲染之间不会扭转的 dispatch 办法,并且您能够在 reducer 中有操作逻辑。还值得注意的是,useState 最初是触发的 update 来更新状态,useReducer 则是用 dispatch 来更新状态。接下来咱们来看这两种钩子函数:useState 和 useReducer 是如何申明和应用的。

用 useState 申明 state

useState 的申明语句十分的简略,例如:

const [state, setState] = useState('default state');

useState 返回一个保留以后 state 和更新 state 的数组,这里的 setState 是更新 state 的函数。

用 useReducer 申明 state

应用 useReducer 的时候看上面的语句:

const [state, dispatch] = useReducer(reducer, initialState)

useReducer 返回一个保留以后 state 和一个更新 state 的 dispatch 函数。这个 dispatch 函数有点相似 setState,咱们在用 setState 更新 state 的时候,是这样用:

<input type='text' value={state} onChange={(e) => setState(e.currentTarget.value)} />

在 onChange 事件中调用 setState 更新以后的 state。比照应用 useReducer 钩子,能够这样表白:

<button onClick={() => dispatch({ type: 'decrement'})}>Decrement</button>

这里的语意是当用户点击按钮的时候,会触发 dispatch,执行 type 是 decrement 的 action。另外在应用 dispatch 函数咱们还能够传 payload:

<button onClick={() => dispatch({ type: 'decrement',payload:0})}>Decrement</button>

咱们晓得 useReducer 能够解决简单多层 state 的状况,这里笔者持续举该类状况的例子:

const [state, dispatch] = useReducer(loginReducer,
  {
    users: [{ username: 'Philip', isOnline: false},
      {username: 'Mark', isOnline: false},
      {username: 'Tope', isOnline: true},
      {username: 'Anita', isOnline: false},
    ],
    loading: false,
    error: false,
  },
);

useReducer 接管一个初始对象,对象的 key 蕴含 users,loading,error。应用 useReducer 治理本地 state 的不便之处是用 useReducer 能够扭转局部的 state,也就是说,这里能够独自扭转 users。

调试 Vue UI 组件太麻烦?

试试卡拉云,无需懂前端,拖拽即可生成前端组件,连贯 API 和数据库间接生成后盾零碎,两个月的工期升高至 1 天

useReducer 用法之能够应用的场景

在开发我的项目的时候,随着咱们工程的体积一直的变大,其中的状态治理会越来越简单,此时咱们最好应用 useReducer。useReducer 提供了比 useState 更可预测的状态治理。当状态治理变的简单的时候,这时候 useReducer 有着比 useState 更好的应用体验。这里的不得不重提一个法令:当你的 state 是根底类型,像 number,boolean,string 等,这时候应用 useState 是一种更简略、更适合的抉择。上面笔者将创立一个登陆的组件,让读者领会应用 useReducer 的益处。

创立一个登陆组件

为了让咱们更好的了解 useReducer 的用法,这里创立一个登陆组件,并比拟一下应用 useState 和 useReducer 在状态治理用法上的异同。首先咱们先用 useState 创立登陆组件:

import React, {useState} from 'react';

export default function LoginUseState() {const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [isLoading, showLoader] = useState(false);
  const [error, setError] = useState('');
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const onSubmit = async (e) => {e.preventDefault();
    setError('');
    showLoader(true);
    try {await function login({ username, password}) {return new Promise((resolve, reject) => {setTimeout(() => {if (username === 'ejiro' && password === 'password') {resolve();
            } else {reject();
            }
          }, 1000);
        });
      }
      setIsLoggedIn(true);
    } catch (error) {setError('Incorrect username or password!');
      showLoader(false);
      setUsername('');
      setPassword('');
    }
  };
  return (
    <div className='App'>
      <div className='login-container'>
        {isLoggedIn ? (
          <>
            <h1>Welcome {username}!</h1>
            <button onClick={() => setIsLoggedIn(false)}>Log Out</button>
          </>
        ) : (<form className='form' onSubmit={onSubmit}>
            {error && <p className='error'>{error}</p>}
            <p>Please Login!</p>
            <input
              type='text'
              placeholder='username'
              value={username}
              onChange={(e) => setUsername(e.currentTarget.value)}
            />
            <input
              type='password'
              placeholder='password'
              autoComplete='new-password'
              value={password}
              onChange={(e) => setPassword(e.currentTarget.value)}
            />
            <button className='submit' type='submit' disabled={isLoading}>
              {isLoading ? 'Logging in...' : 'Log In'}
            </button>
          </form>
        )}
      </div>
    </div>
  );
}

对于 username,password,isLoading 等的治理,都是应用的 useState 进行的解决,所以这里咱们应用了五个 useState 钩子函数,面对更多的 state 的时候,有时候咱们会放心咱们是否能够更好的治理这些 state 呢。这时候能够尝试用 useReducer,间接在 reducer 函数中治理全副的状态。

import React, {useReducer} from 'react';

function loginReducer(state, action) {switch (action.type) {
    case 'field': {
      return {
        ...state,
        [action.fieldName]: action.payload,
      };
    }
    case 'login': {
      return {
        ...state,
        error: '',
        isLoading: true,
      };
    }
    case 'success': {
      return {
        ...state,
        isLoggedIn: true,
        isLoading: false,
      };
    }
    case 'error': {
      return {
        ...state,
        error: 'Incorrect username or password!',
        isLoggedIn: false,
        isLoading: false,
        username: '',
        password: '',
      };
    }
    case 'logOut': {
      return {
        ...state,
        isLoggedIn: false,
      };
    }
    default:
      return state;
  }
}
const initialState = {
  username: '',
  password: '',
  isLoading: false,
  error: '',
  isLoggedIn: false,
};
export default function LoginUseReducer() {const [state, dispatch] = useReducer(loginReducer, initialState);
  const {username, password, isLoading, error, isLoggedIn} = state;
  const onSubmit = async (e) => {e.preventDefault();
    dispatch({type: 'login'});
    try {await function login({ username, password}) {return new Promise((resolve, reject) => {setTimeout(() => {if (username === 'ejiro' && password === 'password') {resolve();
            } else {reject();
            }
          }, 1000);
        });
      }
      dispatch({type: 'success'});
    } catch (error) {dispatch({ type: 'error'});
    }
  };
  return (
    <div className='App'>
      <div className='login-container'>
        {isLoggedIn ? (
          <>
            <h1>Welcome {username}!</h1>
            <button onClick={() => dispatch({ type: 'logOut'})}>
              Log Out
            </button>
          </>
        ) : (<form className='form' onSubmit={onSubmit}>
            {error && <p className='error'>{error}</p>}
            <p>Please Login!</p>
            <input
              type='text'
              placeholder='username'
              value={username}
              onChange={(e) =>
                dispatch({
                  type: 'field',
                  fieldName: 'username',
                  payload: e.currentTarget.value,
                })
              }
            />
            <input
              type='password'
              placeholder='password'
              autoComplete='new-password'
              value={password}
              onChange={(e) =>
                dispatch({
                  type: 'field',
                  fieldName: 'password',
                  payload: e.currentTarget.value,
                })
              }
            />
            <button className='submit' type='submit' disabled={isLoading}>
              {isLoading ? 'Logging in...' : 'Log In'}
            </button>
          </form>
        )}
      </div>
    </div>
  );
}

在应用 useReducer 代替 useState 的过程中,咱们会发现 useReducer 会使咱们更聚焦于 type 和 action,举个例子说,当执行 login 动作的时候,会将 isLoading,error 和 state 进行赋值:

case 'login': {
      return {
        ...state,
        error: '',
        isLoading: true,
      };
    }

体验好的一点是,咱们再也不须要被动去更新 state,useReducer 的赋值会间接帮忙咱们解决所有的问题。

何时该应用 useReducer 实战利用案例

useReducer 最小化的范式

且看上面最简略的例子:

const initialState = 0;
const reducer = (state, action) => {switch (action) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset': return 0;
    default: throw new Error('Unexpected action');
  }
};

代码很简略,首先定义一个初始化的 state:initialState = 0;之后在 reducer 函数中通过 switch 来对 state 执行不同的操作。留神到,这里的 state 其实是个 number 对象,这在 Redux 的使用者看来或者有一些纳闷,因为在 redux 中都是用 object 来解决的。这其实是 useReducer 的不便之处。在组件中,经常会有点击事件带来状态变动的状况,比如说购物车组件中商品数量的减少,点击加号商品数量会加一,这个时候下面的代码就能够利用到组件中,例如:

const Example01 = () => {const [count, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      {count}
      <button onClick={() => dispatch('increment')}>+1</button>
      <button onClick={() => dispatch('decrement')}>-1</button>
      <button onClick={() => dispatch('reset')}>reset</button>
    </div>
  );
};

当用户点击 + 1 的按钮时,dispatch 会登程 increment 的 action,count +1,所以会看到 state 变动后的后果。这种 type 其实能够定义很多,抉择适合的数量即可。

useReducer action 对象

上面的例子其实有点像 redux 的用法,习惯 redux 的同学可能会比拟相熟:

const initialState = {
  count1: 0,
  count2: 0,
};
const reducer = (state, action) => {switch (action.type) {
    case 'increment1':
      return {...state, count1: state.count1 + 1};
    case 'decrement1':
      return {...state, count1: state.count1 - 1};
    case 'set1':
      return {...state, count1: action.count};
    case 'increment2':
      return {...state, count2: state.count2 + 1};
    case 'decrement2':
      return {...state, count2: state.count2 - 1};
    case 'set2':
      return {...state, count2: action.count};
    default:
      throw new Error('Unexpected action');
  }
};

初始化的 state 是一个对象,并且 return 进来的也是一个对象。和后面的那个例子相比,除了多了不同的 case 之外,在更新 state 通过对象赋值的形式进行。initialState 对象中是有两个 key,在更新的时候针对指定的 key 更新即可。下面的例子看起来有些简单,把它用到组件上,会简化应用过程:

const Example02 = () => {const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      <div>
        {state.count1}
        <button onClick={() => dispatch({ type: 'increment1'})}>+1</button>
        <button onClick={() => dispatch({ type: 'decrement1'})}>-1</button>
        <button onClick={() => dispatch({ type: 'set1', count: 0})}>reset</button>
      </div>
      <div>
        {state.count2}
        <button onClick={() => dispatch({ type: 'increment2'})}>+1</button>
        <button onClick={() => dispatch({ type: 'decrement2'})}>-1</button>
        <button onClick={() => dispatch({ type: 'set2', count: 0})}>reset</button>
      </div>
    </>
  );
};

Example2 组件中,上半局部显示的是 count1 的变动,下半局部则是显示 count2 的变动。也是通过点击 button 来触发 dispatch,引起 state 变动。

useReducer 在文本框组件中应用

后面的两个例子都是通过 button 下面的 onClick 事件来触发,在平时的业务开发中,输入框组件的 onChange 事件也是咱们常应用的办法,此时咱们也能够联合 useReducer 来联合输入框的 value 属性应用,做到实时展现输出的内容,使得组件受控,见上面的代码:

const initialState = '';
const reducer = (state, action) => action;

const Example03 = () => {const [firstName, changeFirstName] = useReducer(reducer, initialState);
  const [lastName, changeLastName] = useReducer(reducer, initialState);
  return (
    <>
      <div>
        First Name:
        <TextInput value={firstName} onChangeText={changeFirstName} />
      </div>
      <div>
        Last Name:
        <TextInput value={lastName} onChangeText={changeLastName} />
      </div>
    </>
  );
};

当咱们在 TextInput 组件中自定义 onChangeText 办法,这个时候通过 changeFirstName 函数,扭转 changeFirstName 值,进而扭转 value 值。

useReducer 联合 useContext 应用

在日常的开发中,组件之间共享 state 的时候,很多人应用全局的 state,尽管这样能够满足需要,然而升高了组件的灵活性和扩展性,所以更优雅的一种形式是应用 useContext,对于 useContext 不相熟的同学能够参考 react 官网文档对于这一部分的解说。在本例子中,笔者将应用 useContext 和 useReducer 函数一起应用,看上面的代码:

const CountContext = React.createContext();

const CountProvider = ({children}) => {const contextValue = useReducer(reducer, initialState);
  return (<CountContext.Provider value={contextValue}>
      {children}
    </CountContext.Provider>
  );
};

const useCount = () => {const contextValue = useContext(CountContext);
  return contextValue;
};

useCount 函数是自定义的 hook,和失常的 hook 应用的形式是统一的。那么组件在应用 useCount 钩子的时候,能够像上面这样用:

const Counter = () => {const [count, dispatch] = useCount();
  return (
    <div>
      {count}
      <button onClick={() => dispatch({ type: 'increment'})}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement'})}>-1</button>
      <button onClick={() => dispatch({ type: 'set', count: 0})}>reset</button>
    </div>
  );
};

// now use it
const Example05 = () => (
  <>
    <CountProvider>
      <Counter />
      <Counter />
    </CountProvider>
    <CountProvider>
      <Counter />
      <Counter />
    </CountProvider>
  </>
);

useCount 会走外部的 useReducer, 这个时候通过 dispatch 函数会扭转对应的 state 的状态。

useReducer 订阅的须要

Context 应用的场景其实是在组件之间,然而如果在组件的内部,这个时候咱们须要应用订阅来做。这个时候咱们能够订阅一个共享的 state,并当 state 更新的时候去更新组件。对于后面的那个应用 Context 的例子,这里咱们用订阅实现一下。第一步,首先写一个最简略的 useReducer:

const useForceUpdate = () => useReducer(state => !state, false)[1];

接下里写一个函数创立共享的 state 并返回一个钩子函数:

const createSharedState = (reducer, initialState) => {const subscribers = [];
  let state = initialState;
  const dispatch = (action) => {state = reducer(state, action);
    subscribers.forEach(callback => callback());
  };
  const useSharedState = () => {const forceUpdate = useForceUpdate();
    useEffect(() => {const callback = () => forceUpdate();
      subscribers.push(callback);
      callback(); // in case it's already updated
      const cleanup = () => {const index = subscribers.indexOf(callback);
        subscribers.splice(index, 1);
      };
      return cleanup;
    }, []);
    return [state, dispatch];
  };
  return useSharedState;
};

这里咱们应用了 useEffect 钩子函数,在这个钩子函数中,咱们订阅一个回调函数来更新组件,当组件卸载的时候,咱们也会革除订阅。接下来咱们创立两个共享的 state:

const useCount1 = createSharedState(reducer, initialState);
const useCount2 = createSharedState(reducer, initialState);

用一下这个钩子函数:

const Counter = ({count, dispatch}) => (
  <div>
    {count}
    <button onClick={() => dispatch({ type: 'increment'})}>+1</button>
    <button onClick={() => dispatch({ type: 'decrement'})}>-1</button>
    <button onClick={() => dispatch({ type: 'set', count: 0})}>reset</button>
  </div>
);

const Counter1 = () => {const [count, dispatch] = useCount1();
  return <Counter count={count} dispatch={dispatch} />
};

const Counter2 = () => {const [count, dispatch] = useCount2();
  return <Counter count={count} dispatch={dispatch} />
};

最初咱们用一个函数组件封装 Counter:

const Example06 = () => (
  <>
    <Counter1 />
    <Counter1 />
    <Counter2 />
    <Counter2 />
  </>
);

这里的 count 的更新都是应用共享的 useCount 钩子函数。

useReducer 用法之不该应用的场景

这是一个好的问题,后面介绍了应用 useReducer 的状况,然而什么时候咱们不能够用 useReducer 呢。为了更好的了解这个问题,笔者首先说一下应用 useReducer 根本的心智,useReducer 是能够帮忙咱们治理简单的 state , 然而咱们也不应该疏忽 redux 在某些状况下可能是更好的抉择。最开始咱们的想法是咱们尽量避免应用第三方的 state 管理工具,当你有纳闷是否要应用他们时,阐明这不是用他们的时候。上面笔者列举几个应用 Redux 和 Mobx 的例子。

当你的利用须要繁多的起源时

以后端的利用通过接口获取数据,且这个数据源就是从这个接口获取的,这个时候应用 Redux 能够更不便的治理咱们的 state,就像是写一个 todo/undo demo,间接能够应用 Redux。

当你须要一个更可预测的状态

当你的利用运行在不同的环境中时,应用 Redux 能够使得 state 的治理变得更稳固。同样的 state 和 action 传到 reducer 的时候,会返回雷同的后果。并且 redux 不会带来副作用,只有 action 会使其更改状态。

当状态晋升到顶部组件

当须要在顶部组件解决所有的状态的时候,这时候应用 Redux 是更好的抉择。

React useReducer 教程总结

到这里 useReducer 的应用场景和用法例子解说都曾经介绍实现了,最初咱们回顾一下,首先类比于 redux 的 reducer,useReducer 的思路和 redux 一样,不同点是在于 useReducer 最终操作的对象是 state。在应用上,就拿最简略的 button 组件为例子,点击的时候触发 dispatch,依据 type 批改 state。简单一点的,能够联合 useContext 应用,满足多个组件共享 state 的状况。总之,在把握用法之后,多在我的项目中实际,learn by doing,是较为无效的把握常识的形式。

其实如果你基本不想解决简单的 React 前端问题,齐全能够应用卡拉云来搭建前端工具,卡拉云内置多种罕用组件,无需懂任何前端,仅需拖拽即可疾速生成。

上面是用卡拉云搭建的数据库 CURD 后盾管理系统,只需拖拽组件,即可在 10 分钟内实现搭建。

可间接分享给共事一起应用:https://my.kalacloud.com/apps/8z9z3yf9fy/published

卡拉云可帮你疾速搭建企业外部工具,下图为应用卡拉云搭建的外部广告投放监测零碎,无需懂前端,仅需拖拽组件,10 分钟搞定。你也能够疾速搭建一套属于你的后盾管理工具。

卡拉云是新一代低代码开发平台,与前端框架 Vue、React 等相比,卡拉云的劣势在于不必首先搭建开发环境,间接注册即可开始应用。开发者齐全不必解决任何前端问题,只需简略拖拽,即可疾速生成所需组件,可一键接入常见数据库及 API,依据疏导简略几步买通前后端,数周的开发工夫,缩短至 1 小时。立刻收费试用卡拉云。

扩大浏览:

  • React Router 6 教程
  • React 表格教程
  • React 后盾评测
正文完
 0