121.Flux 和 Redux 之间有什么区别?
以下是 Flux 和 Redux 之间的主要区别
Flux | Redux |
---|---|
状态是可变的 | 状态是不可变的 |
Store 包含状态和更改逻辑 | 存储和更改逻辑是分开的 |
存在多个 Store | 仅存在一个 Store |
所有的 Store 都是断开连接的 | 带有分层 reducers 的 Store |
它有一个单独的 dispatcher | 没有 dispatcher 的概念 |
React 组件监测 Store | 容器组件使用连接函数 |
122.mapStateToProps()
和 mapDispatchToProps()
之间有什么区别?
mapStateToProps()
是一个实用方法,它可以帮助您的组件获得最新的状态(由其他一些组件更新):
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
mapDispatchToProps()
是一个实用方法,它可以帮助你的组件触发一个动作事件(可能导致应用程序状态改变的调度动作):
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
123.如何在组件外部访问 Redux 存储的对象?
使用connect连接 React 组件与 Redux store。连接操作不会改变原来的组件类。反而返回一个新的已与 Redux store 连接的组件类。
import { connect } from 'react-redux'
//将state状态映射到属性里面,之后可以通过props获取
const mapStatetoProps=(state)=>{
return {num:state.counter,city:state.city}
}
//addFn 自动有了dispatch的功能 onClick={addFn} ; addFn minusFn minusFn会被映射到props里面
const mapDispatchToProps={addFn,minusFn,addAsynFn,changeCityFn}
//为App组件提供数据和逻辑。mapStateToProps负责将state的数据映射到展示组件的this.props。mapDispatchToProps负责定义发送action的函数映射到展示组件的this.props
App=connect(mapStatetoProps,mapDispatchToProps)(App)
export default App
124.Redux 中的 Action 是什么?
Actions是纯 JavaScript 对象或信息的有效负载,可将数据从您的应用程序发送到您的 Store。 它们是 Store 唯一的数据来源。 Action 必须具有指示正在执行的操作类型的 type 属性。
例如,表示添加新待办事项的示例操作:
{
type: ADD_TODO,
text: 'Add todo item'
}
125.如何在加载时触发 Action?
您可以在componentDidMount()
方法中触发 Action,然后在render()
方法中可以验证数据。
class App extends Component {
componentDidMount() {
this.props.fetchData()
}
render() {
return this.props.isLoaded
? <div>{'Loaded'}</div>
: <div>{'Not Loaded'}</div>
}
}
const mapStateToProps = (state) => ({
isLoaded: state.isLoaded
})
const mapDispatchToProps = { fetchData }
export default connect(mapStateToProps, mapDispatchToProps)(App)
126.在 React 中如何使用 Redux 的 connect()
?
您需要按照两个步骤在容器中使用您的 Store:
-
使用mapStateToProps(): 它将 Store 中的状态变量映射到您指定的属性。
-
将上述属性连接到容器:
mapStateToProps
函数返回的对象连接到容器。你可以从react-redux
导入connect()
。import React from 'react' import { connect } from 'react-redux' class App extends React.Component { render() { return <div>{this.props.containerData}</div> } } function mapStateToProps(state) { return { containerData: state.data } } export default connect(mapStateToProps)(App)
127.如何在 Redux 中重置状态?
最简单的实现方法就是为每个独立的 store 添加RESET_APP
的 action,每次需要 reset 的时候,dispatch 这个 action 即可,如下代码
const usersDefaultState = []
const users = (state = usersDefaultState, { type, payload }) => {
switch (type) {
case "RESET_APP":
return usersDefaultState;
case "ADD_USER":
return [...state, payload];
default:
return state;
}
};
这样虽然简单,但是当独立的 store 较多时,需要添加很多 action,而且需要很多个 dispatch 语句去触发
dispatch({ type: RESET_USER });
dispatch({ type: RESET_ARTICLE });
dispatch({ type: RESET_COMMENT });
不过这里一种更优雅的实现,需要用到一个小技巧,看下面代码:
const usersDefaultState = []
const users = (state = usersDefaultState, { type, payload }) => {...}
当函数参数 state 为 undefined 时,state 就会去 usersDefaultState 这个默认值,利用这个技巧,我们可以在 rootReducers 中检测 RESET_DATA action,直接赋值 undefined 就完成了所有 store 的数据重置。实现代码如下:
我们通常这样导出所有的 reducers
// reducers.js
const rootReducer = combineReducers({
/* your app’s top-level reducers */
})
export default rootReducer;
先封装一层,combineReducers 返回 reducer 函数,不影响功能
// reducers.js
const appReducer = combineReducers({
/* your app’s top-level reducers */
})
const rootReducer = (state, action) => {
return appReducer(state, action)
}
export default rootReducer;
检测到特定重置数据的 action 后利用 undefined 技巧 (完整代码)
// reducers.js
const appReducer = combineReducers({
/* your app’s top-level reducers */
})
const rootReducer = (state, action) => {
if (action.type === 'RESET_DATA') {
state = undefined
}
return appReducer(state, action)
}
128.React 上下文和 React Redux 之间有什么区别?
您可以直接在应用程序中使用Context,这对于将数据传递给深度嵌套的组件非常有用。而Redux功能更强大,它还提供了 Context API 无法提供的大量功能。此外,React Redux 在内部使用上下文,但它不会在公共 API 中有所体现。
129.如何在 Redux 中发起 AJAX 请求?
当在redux中有异步操作的时候,可以使用redux-thunk
中间件,它允许您定义异步操作。
让我们举个例子,使用fetch API将特定帐户作为 AJAX 调用获取:
export function fetchAccount(id) {
return dispatch => {
dispatch(setLoadingAccountState()) // Show a loading spinner
fetch(`/account/${id}`, (response) => {
dispatch(doneFetchingAccount()) // Hide loading spinner
if (response.status === 200) {
dispatch(setAccount(response.json)) // Use a normal function to set the received state
} else {
dispatch(someError)
}
})
}
}
function setAccount(data) {
return { type: 'SET_Account', data: data }
}
130.我需要将所有状态保存到 Redux 中吗?我应该使用 react 的内部状态吗?
这取决于开发者的决定。一般情况下,组件UI的状态控制的数据我们可以放在组件内部声明。而如果有多个组件要共享的数据、该数据有多个派生数据、该数据需要缓存等情况我们可以放在redux中
131.React Redux 中展示组件和容器组件之间的区别是什么?
展示组件是一个类或功能组件,用于描述应用程序的展示部分(没有使用connect包裹的组件是展示组件)
容器组件是连接到 Redux Store的组件的非正式术语(使用connect包裹之后返回的新组件)。容器组件订阅 Redux 状态更新和dispatch操作,它们通常不呈现 DOM 元素;他们将渲染委托给展示性的子组件。
132.Redux 中常量的用途是什么?
常量允许您在使用 IDE 时轻松查找项目中该特定功能的所有用法。它还可以防止你拼写错误,在这种情况下,你会立即得到一个ReferenceError
。
通常我们会将它们保存在一个文件中(constants.js
或actionTypes.js
)。
export const ADD_TODO = 'ADD_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const EDIT_TODO = 'EDIT_TODO'
export const COMPLETE_TODO = 'COMPLETE_TODO'
export const COMPLETE_ALL = 'COMPLETE_ALL'
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
在 Redux 中,您可以在两个地方使用它们:
-
在 Action 创建时:
让我们看看
actions.js
:import { ADD_TODO } from './actionTypes'; export function addTodo(text) { return { type: ADD_TODO, text } }
-
在 reducers 里:
让我们创建
reducer.js
文件:import { ADD_TODO } from './actionTypes' export default (state = [], action) => { switch (action.type) { case ADD_TODO: return [ ...state, { text: action.text, completed: false } ]; default: return state } }
133.编写 mapDispatchToProps()
有哪些不同的方法?
有一些方法可以将action creators绑定到mapDispatchToProps()
中的dispatch()
。以下是可能的写法:
const mapDispatchToProps = (dispatch) => ({
action: () => dispatch(action())
})
const mapDispatchToProps = (dispatch) => ({
action: bindActionCreators(action, dispatch)
})
const mapDispatchToProps = { action }
第三种写法只是第一种写法的简写。
134.如何构建 Redux 项目目录?
大多数项目都有几个顶级目录,如下所示:
- Components: 用于dumb组件,Redux 不必关心的组件。
- Containers: 用于连接到 Redux 的smart组件。
- Actions: 用于所有 Action 创建器,其中文件名对应于应用程序的一部分。
- Reducers: 用于所有 reducer,其中文件名对应于state key。
- Store: 用于 Store 初始化。
这种结构适用于中小型项目。
135.什么是 redux-saga?
redux-saga
是一个库,旨在解决 React/Redux 项目中异步问题(数据获取等异步操作和访问浏览器缓存等可能产生副作用的动作)更容易,更好。
这个包在 NPM 上有发布:
$ npm install --save redux-saga
136.在 redux-saga 中 call()
和 put()
之间有什么区别?
put: 用于触发action
yield put({ type: 'todos/add', payload: 'Learn Dva'});
call:用于调用异步逻辑,支持Promise。
const result = yield call(fetch, '/todos');
这个call与JS的call用法大概一致,这个call的第一个参数是你要调用的函数,第二个参数开始是你要传递的参数,可一 一传递。
effects: {
// 访问接口获取数据 并且保存数据
*fetchUserList({ payload: { page = 1 } }, { call, put }) {
const { data } = yield call(queryUserList, { page });
yield put({
type: 'save',
payload: {
list: data.list,
total: parseInt(data.total, 10),
page: parseInt(data.page, 10)
}
});
},
},
137.什么是 Redux Thunk?
redux-thunk是一个redux的中间件,用来处理redux中的复杂逻辑,比如异步请求;
redux-thunk
中间件可以让action
创建函数先不返回一个action
对象,而是返回一个函数;
138.redux-saga
和 redux-thunk
之间有什么区别?
Redux Thunk和Redux Saga都负责处理副作用。在大多数场景中,Thunk 使用Promises来处理它们,而 Saga 使用Generators。Thunk 易于使用,因为许多开发人员都熟悉 Promise,Sagas/Generators 功能更强大,但您需要学习它们。但是这两个中间件可以共存,所以你可以从 Thunks 开始,并在需要时引入 Sagas。
139.如何向 Redux 添加多个中间件?
你可以使用applyMiddleware()
。
例如,你可以添加redux-thunk
和logger
作为参数传递给applyMiddleware()
:
import { createStore, applyMiddleware } from 'redux'
const createStoreWithMiddleware = applyMiddleware(ReduxThunk, logger)(createStore)
140.如何在 Redux 中设置初始状态?
您需要将初始状态作为第二个参数传递给 createStore :
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
})
const initialState = {
todos: [{ id: 123, name: 'example', completed: false }]
}
const store = createStore(
rootReducer,
initialState
)