受控组件和非受控组件是 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>
);
}
在 Redux 中处理异步请求通常使用中间件来实现。最常用的中间件是 Redux Thunk 和 Redux Saga。
首先,安装 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 传递。
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 传递。
Redux 状态管理器和将变量挂载到 window 对象中都是在应用程序中共享和访问数据的方式,但它们有一些区别:
综上所述,Redux 状态管理器提供了一种规范的、可预测的数据管理方式,适用于复杂的应用程序,并具有良好的可维护性和扩展性。而将变量挂载到 window 对象中是一种更简单的全局共享数据方式,适用于简单的应用程序或临时共享数据的场景,但同时也存在命名冲突和可维护性等问题。因此,根据具体的应用场景和需求,选择适合的数据共享方式是很重要的。
在 Redux 中,connect 函数是用于连接 Redux store 和组件的高阶函数。它的作用是将组件与 Redux store 进行绑定,使组件能够访问 Redux store 中的状态(state)和派发(dispatch)操作。
connect 函数接受两个参数:
mapStateToProps:一个函数,用于指定需要从 Redux store 中提取的状态数据,并将其映射到组件的 props 上。它接收 Redux store 中的状态作为参数,并返回一个对象。对象的属性将成为组件的 props,并且其值将是通过 mapStateToProps 函数从 Redux store 中提取的状态数据。
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 进行集成,并在需要时获取和操作状态数据。
在 React 中,useState 是一种 React Hooks,用于在函数组件中添加状态。它返回一个包含两个元素的数组,而不是一个对象,有以下原因:
简化语法:使用数组的形式可以使 useState 的语法更加简洁和直观。数组的元素可以直接通过解构赋值来获取,而不需要使用对象的键。这样可以减少代码的复杂性和冗余。
顺序保持一致:useState 返回的数组的第一个元素是当前状态的值,第二个元素是用于更新状态的函数。这种顺序保持了 useState 在多次调用时的一致性,不会因为状态名称的不同而导致混乱。无论 useState 被调用多少次,获取状态值的方式都是 const [value, setValue] = useState(initialValue)。
可读性:使用数组的形式,可以更清晰地表达 useState 的含义。从语义上来说,返回的数组中的第一个元素表示状态的值,第二个元素表示更新状态的函数,更符合直觉和可读性。
尽管 useState 返回的是数组,但可以使用解构赋值来给返回的值取一个更具描述性的名称,如 const [count, setCount] = useState(0),这样可以更好地理解和使用状态值和更新函数。
总结起来,useState 返回数组而不是对象,是为了语法简洁、顺序一致和可读性。这种设计使得在函数组件中添加状态变得更加直观和易于理解。