此文用最速的方法使用react-redux包,省略大量的细节,跑通使用react-redux控制的一个组件。
使用redux的基本原因,是为了保证单向数据流和集中状态管理。
所有的状态都保存在context中的总体状态(被称为store)里,这样所有组件都访问唯一的store。而组件本身不管理状态,只是向这个store发送action,而发送的action会被reducer处理,进而修改store。store如果被修改,则向页面发送props,令页面重新渲染(大致流向,用词不准确)。如此,数据流成为了单一的循环,避免了组件之间互相通信,也避免了mvc中的view和model通信(即页面本身绝对不会引发数据库的活动,必须派发action给管理中心store)
接下来这个例子是用react-redux操控component中一个button点击更改文字的例子:
./src
|-- index.js
|-- App.js
|-- ./actions
|-- index.js
|-- user.js
|-- ./reducers
|-- index.js
|-- user.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './reducers';
import App from './App';
let initialStore = {
};
const store = createStore(rootReducer, initialStore);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
解释:
export * from "./user";
./actions/user.js中写了一个用来制造action的构造函数,这个构造函数输入一个参数名为userName,输出一个action object,包含两个key,一个为type,用来讲明action的类型,另一个为userName,即将传入的参数赋值给userName的key
export function changeUserName({ userName }) {
return {
type: "CHANGE_USER_NAME",
userName: userName
};
}
一个action就是一个object,这个object里面表达了这个action的type以及要将某一个数据a依据传入的参数变成更新后的数据a’,说白了就是一个动作肯定有一个这个动作的名字,叫type,此外,该动作就是想要依据输入参数改变某个属性的输出。
const initialState = {
username: 'alex',
}
const user = (state = initialState, action) => {
switch (action.type) {
case 'CHANGE_USER_NAME':
return {
...state,
username: action.userName
}
default:
return state;
}
}
export default user;
这是一个名为user的reducer,它是用来处理与user相关的action的,它作为一个函数接受两个参数,一个是state,一个是action,而它返回一个被修改后的state。可见,所谓reducer,本质上就是一个用action里面的值去替换state里面的属性值的函数,其功能相当于component里面的this.setState。
reducer的输入是原来的state和action,其返回值,就是一个修改后的state。
./reducers/index.js中
import { combineReducers } from "redux";
import user from "./user";
export default combineReducers({
user
});
解释:从redux中引入了combineReducers方法用来把所有的reducer合并为一个reducer,这个经过合并的reducer,实际上就在根目录下的index.js文件中被引入,是用来createStore的那个rootReducer。简单来说,reducer被建立好以后就是用来createStore用的(回头看看最开始单向数据流的那张图)。
5. 在./src/App.js中
import React from 'react';
import { connect } from 'react-redux';
import { changeUserName } from './actions';
class App extends React.Component {
changeUserName = () => {
const { changeUserName } = this.props;
changeUserName({
userName: "bruce"
})
}
render() {
const { username } = this.props;
console.log(this.props)
return (
<>
<h1>{username}</h1>
<button type="button" onClick={this.changeUserName}>change</button>
</>
)
}
}
function mapStateToProps(state) {
const { user } = state;
return {
username: user.username,
}
}
export default connect(mapStateToProps, { changeUserName })(App);
解释:App中最关键的点在于connect,这个方法是从react-redux库中导入的。具体实现的机制不谈,这里要记住的是:
App能够从props里面得到当前的state依靠的是第一个函数mapStateToProps,我个人感觉理解react-redux最难的点就在这个connect上,这个connect本质上把两个object合并成了props这个object。前面一个object是mapStateToProps,即将state中的属性值取出变成this.props中要用的属性值。而在当前的state里面,其数据结构是state = {reducer名:{属性名:属性值}}。而connect的第二个参数则是传入另一个{},这个object里面将放入action creator也就是要使用的action的构造函数。其结果就是现在的this.props里面既包含了从state放下来的数据,又包含了action的构造函数,也就是把改造数据用的工具都囊括在connect里面。
最后,从this.props里面可以取出action的构造函数,在点击事件触发的函数里面,用构造函数生成一个action的实例。当点击事件发生时,这个action就被创建,然后就会因此改变store;另外从this.props里面还能够取出username(已经经过mapStatetoProps处理),这个值将被运用于页面渲染。
循环的流程始于点击事件触发action,action通过构造函数被创建,随后进入reducer修改了state,state改变引发重新渲染,mapStateToProps重新给props中的username赋值,页面重新渲染。