【React】精选5题

1. 说说对受控组件和非受控组件的理解,以及应用场景?

受控组件和非受控组件是 React 中关于表单组件的两种不同的实现方式。

受控组件:

受控组件是指表单元素的值由 React 组件的状态(state)来控制。
通过在表单元素上设置 value 属性,并在 onChange 事件中更新组件的状态,从而实现对表单元素值的控制。
受控组件的值完全由 React 组件控制,因此可以实时获取和更新表单元素的值。
适用于需要实时处理表单元素值的场景,例如实时验证输入、基于表单值的计算等。

下面是一个使用受控组件的示例:

import React, { useState } from 'react';

function ControlledComponentExample() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    // 在提交表单时可以获取最终的表单值进行处理
    console.log('Form value:', value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

非受控组件:

非受控组件是指表单元素的值由 DOM 自身管理,而不受 React 组件状态的控制。
在非受控组件中,可以通过 ref 属性来获取表单元素的值。
非受控组件适用于不需要实时处理表单元素值的场景,例如只在表单提交时获取表单值、使用原生 JavaScript 或其他库处理表单值等。

下面是一个使用非受控组件的示例:

import React, { useRef } from 'react';

function UncontrolledComponentExample() {
  const inputRef = useRef();

  const handleSubmit = (event) => {
    event.preventDefault();
    // 在提交表单时通过 ref 获取表单元素的值进行处理
    console.log('Form value:', inputRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" ref={inputRef} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

应用场景:

  1. 受控组件适用于需要对表单元素值进行实时处理的场景,例如实时验证输入、基于表单值的计算等。受控组件可以通过组件状态来控制表单元素值的获取和更新,从而实现实时响应。
  2. 非受控组件适用于不需要实时处理表单元素值的场景,例如只在表单提交时获取表单值、使用原生 JavaScript 或其他库处理表单值等。非受控组件可以更简洁地获取表单元素的值,无需维护组件状态。
    总结:
    受控组件和非受控组件都是 React 中处理表单元素的方式,选择受控组件还是非受控组件取决于具体的应用场景和需求。受控组件适用于需要实时处理表单元素值的场景,而非受控组件适用于不需要实时处理表单元素值的场景。

2. Redux 中异步的请求怎么处理

在 Redux 中处理异步请求通常使用中间件来实现。最常用的中间件是 Redux Thunk 和 Redux Saga。

  1. 使用 Redux Thunk:
    Redux Thunk 是 Redux 的官方中间件,它使得 Redux 可以处理异步操作。

首先,安装 redux-thunk:

npm install redux-thunk

创建一个 thunk action:

// actions.js
import axios from 'axios';

export const fetchUserData = () => {
  return (dispatch) => {
    dispatch({ type: 'FETCH_USER_DATA_REQUEST' });

    axios.get('/api/user')
      .then((response) => {
        dispatch({ type: 'FETCH_USER_DATA_SUCCESS', payload: response.data });
      })
      .catch((error) => {
        dispatch({ type: 'FETCH_USER_DATA_FAILURE', payload: error.message });
      });
  };
};

创建一个 redux store 并应用 Redux Thunk 中间件:

// store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware(thunk));

在组件中使用 thunk action:

// MyComponent.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUserData } from './actions';

const MyComponent = () => {
  const dispatch = useDispatch();
  const userData = useSelector((state) => state.userData);

  useEffect(() => {
    dispatch(fetchUserData());
  }, [dispatch]);

  if (userData.loading) {
    return <div>Loading...</div>;
  }

  if (userData.error) {
    return <div>Error: {userData.error}</div>;
  }

  return (
    <div>
      <h1>User Data</h1>
      <p>Name: {userData.user.name}</p>
      <p>Email: {userData.user.email}</p>
    </div>
  );
};

export default MyComponent;

这样就可以在 Redux 中处理异步请求了。当调用 fetchUserData action 时,会先发送一个请求并 dispatch FETCH_USER_DATA_REQUEST action,请求成功后 dispatch FETCH_USER_DATA_SUCCESS action,并将返回的数据作为 payload 传递。如果请求失败,会 dispatch FETCH_USER_DATA_FAILURE action,并将错误消息作为 payload 传递。

  1. 使用 Redux Saga:
    Redux Saga 是一个用于管理 Redux 应用中副作用(如异步请求和访问浏览器缓存)的库。
    首先,安装 redux-saga:
npm install redux-saga

创建一个 saga:

// sagas.js
import { put, call, takeEvery } from 'redux-saga/effects';
import axios from 'axios';

function* fetchUserData(action) {
  try {
    yield put({ type: 'FETCH_USER_DATA_REQUEST' });

    const response = yield call(axios.get, '/api/user');

    yield put({ type: 'FETCH_USER_DATA_SUCCESS', payload: response.data });
  } catch (error) {
    yield put({ type: 'FETCH_USER_DATA_FAILURE', payload: error.message });
  }
}

function* userSaga() {
  yield takeEvery('FETCH_USER_DATA', fetchUserData);
}

export default userSaga;

创建一个 redux store 并应用 Redux Saga 中间件:

// store.js
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './reducers';
import userSaga from './sagas';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(userSaga);

在组件中使用 saga action:

// MyComponent.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';

const MyComponent = () => {
  const dispatch = useDispatch();
  const userData = useSelector((state) => state.userData);

  useEffect(() => {
    dispatch({ type: 'FETCH_USER_DATA' });
  }, [dispatch]);

  if (userData.loading) {
    return <div>Loading...</div>;
  }

  if (userData.error) {
    return <div>Error: {userData.error}</div>;
  }

  return (
    <div>
      <h1>User Data</h1>
      <p>Name: {userData.user.name}</p>
      <p>Email: {userData.user.email}</p>
    </div>
     );
   };

   export default MyComponent;

这样就可以在 Redux 中使用 Redux Saga 处理异步请求了。当调用 { type: ‘FETCH_USER_DATA’ } action 时,Saga 会捕获该 action,执行 fetchUserData saga,并在其中处理异步请求。请求开始前会 dispatch FETCH_USER_DATA_REQUEST action,请求成功后 dispatch FETCH_USER_DATA_SUCCESS action,并将返回的数据作为 payload 传递。如果请求失败,会 dispatch FETCH_USER_DATA_FAILURE action,并将错误消息作为 payload 传递。

3. Redux 状态管理器和变量挂载到 window 中有什么区别?

Redux 状态管理器和将变量挂载到 window 对象中都是在应用程序中共享和访问数据的方式,但它们有一些区别:

  1. 数据共享范围:
    Redux 状态管理器(如 Redux store)提供了一种在整个应用程序中共享和访问数据的一致方式。通过将数据存储在 Redux store 中,可以在应用程序的任何组件中访问和更新数据,而不需要手动传递数据或使用 props 进行数据传递。Redux 使用单一的状态树来管理应用程序的状态,使得数据的状态更加可控和可预测。
    将变量挂载到 window 对象中是一种全局共享数据的方式,可以在整个应用程序的任何地方访问和使用该变量。通过将变量挂载到 window 对象中,可以避免在组件层级间传递数据,但同时也失去了数据的封装和控制。挂载到 window 对象中的变量可能会被其他代码修改或覆盖,容易引起命名冲突或不可预测的行为。
  2. 数据管理和访问方式:
    Redux 提供了一套明确定义的规则和方法来管理和访问数据。通过定义 actions、reducers 和 selectors,您可以在应用程序中组织和处理数据的更新逻辑,并通过 connect、useSelector 等方法将数据连接到组件上。Redux 的状态管理是可预测和可测试的,并且可以与其他中间件和工具进行良好的集成。
    将变量挂载到 window 对象中是一种更自由的方式,但同时也意味着数据的更新和访问逻辑需要自行管理和实现。在多个组件之间共享数据时,需要手动获取和更新 window 对象中的变量,这可能导致代码的可读性和可维护性下降。此外,由于 window 对象是全局共享的,变量的命名应该避免与其他代码冲突。

综上所述,Redux 状态管理器提供了一种规范的、可预测的数据管理方式,适用于复杂的应用程序,并具有良好的可维护性和扩展性。而将变量挂载到 window 对象中是一种更简单的全局共享数据方式,适用于简单的应用程序或临时共享数据的场景,但同时也存在命名冲突和可维护性等问题。因此,根据具体的应用场景和需求,选择适合的数据共享方式是很重要的。

4. Redux中的connect有什么作用?

在 Redux 中,connect 函数是用于连接 Redux store 和组件的高阶函数。它的作用是将组件与 Redux store 进行绑定,使组件能够访问 Redux store 中的状态(state)和派发(dispatch)操作。

connect 函数接受两个参数:

  1. mapStateToProps:一个函数,用于指定需要从 Redux store 中提取的状态数据,并将其映射到组件的 props 上。它接收 Redux store 中的状态作为参数,并返回一个对象。对象的属性将成为组件的 props,并且其值将是通过 mapStateToProps 函数从 Redux store 中提取的状态数据。

  2. mapDispatchToProps:一个对象或函数,用于指定将要派发的操作,并将其映射到组件的 props 上。对象中的属性将成为组件的 props,并且其值应该是一个派发操作的函数(即 action creators)。如果传递的是一个函数,它将接收 dispatch 作为参数,并且应返回一个对象。

通过 connect 函数的调用,它返回一个高阶组件(Higher-Order Component),并将被连接的组件作为参数传递给高阶组件。高阶组件会在内部订阅 Redux store 的变化,并在状态更新时触发组件的重新渲染。

使用 connect 的示例代码如下:

import { connect } from 'react-redux';
import { incrementCounter } from '../actions';

const Counter = ({ count, increment }) => {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

const mapStateToProps = (state) => {
  return {
    count: state.counter
  };
};

const mapDispatchToProps = {
  increment: incrementCounter
};

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

在上述示例中,通过 connect 函数将 Counter 组件连接到 Redux store。mapStateToProps 函数将 Redux store 中的 counter 状态映射到组件的 count 属性上,mapDispatchToProps 对象将 incrementCounter 操作映射到组件的 increment 属性上。最后,通过调用 connect(mapStateToProps, mapDispatchToProps)(Counter),返回一个新的组件,该组件已经与 Redux store 进行了绑定。

通过 connect 函数的使用,组件可以访问 Redux store 中的状态并根据需要更新组件。这使得组件能够与 Redux 进行集成,并在需要时获取和操作状态数据。

5. 为什么 useState 返回的是数组而不是对象?

在 React 中,useState 是一种 React Hooks,用于在函数组件中添加状态。它返回一个包含两个元素的数组,而不是一个对象,有以下原因:

  1. 简化语法:使用数组的形式可以使 useState 的语法更加简洁和直观。数组的元素可以直接通过解构赋值来获取,而不需要使用对象的键。这样可以减少代码的复杂性和冗余。

  2. 顺序保持一致:useState 返回的数组的第一个元素是当前状态的值,第二个元素是用于更新状态的函数。这种顺序保持了 useState 在多次调用时的一致性,不会因为状态名称的不同而导致混乱。无论 useState 被调用多少次,获取状态值的方式都是 const [value, setValue] = useState(initialValue)。

  3. 可读性:使用数组的形式,可以更清晰地表达 useState 的含义。从语义上来说,返回的数组中的第一个元素表示状态的值,第二个元素表示更新状态的函数,更符合直觉和可读性。

尽管 useState 返回的是数组,但可以使用解构赋值来给返回的值取一个更具描述性的名称,如 const [count, setCount] = useState(0),这样可以更好地理解和使用状态值和更新函数。

总结起来,useState 返回数组而不是对象,是为了语法简洁、顺序一致和可读性。这种设计使得在函数组件中添加状态变得更加直观和易于理解。

你可能感兴趣的:(react.js,javascript,前端)