React、Redux的基本使用

一、相关知识点

1.React是什么?

React 是一个用于构建用户界面的 JAVASCRIPT 库。

2.React 特点

1.声明式设计 −React采用声明范式,可以轻松描述应用。

2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

3.灵活 −React可以与已知的库或框架很好地配合。

4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

3.生命周期
在这里插入图片描述
这篇文章的生命周期写的挺好的,感兴趣的小伙伴可以去看一下https://www.jianshu.com/p/514fe21b9914

4.jsx

React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。
由于 JSX 就是 JavaScript,一些标识符像 classfor 不建议作为 XML 属性名。作为替代,React DOM 使用 className 和 htmlFor 来做对应的属性。

优点:

JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
它是类型安全的,在编译过程中就能发现错误。
使用 JSX 编写模板更加简单快速。

5.react渲染过程

1. state 数据
2. JSX 模版
3. 数据 + 模版 结合,生成真实的DOM,来显示
4. state 发生改变
5. 数据 + 模版 结合,生成真实的DOM,替换原始的DOM

缺陷:
第一次生成了一个完整的DOM片段
第二次生成了一个完整的DOM片段
第二次的DOM替换第一次的DOM,非常耗性能

1. state 数据
2. JSX 模版
3. 数据 + 模版 结合,生成真实的DOM,来显示
4. state 发生改变
5. 数据 + 模版 结合,生成真实的DOM,并不直接替换原始的DOM
6. 新的DOM(DoucumentFragment) 和原始的DOM 做比对,找差异
7. 找出input框发生了变化
8. 只用新的DOM中的input元素,替换掉老的DOM中的input元素

缺陷:
性能的提升并不明显
1. state 数据
2. JSX 模版

3. 数据 + 模版 生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实DOM)(损耗了性能)
['div', {id: 'abc'}, ['span', {}, 'hello world']]

4. 用虚拟DOM的结构生成真实的DOM,来显示 
<div id='abc'><span>hello world</span></div>

5. state 发生变化

6. 数据 + 模版 生成新的虚拟DOM (极大的提升了性能)
['div', {id: 'abc'}, ['span', {}, 'bye bye']]

7. 比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中内容(极大的提升性能)

8. 直接操作DOM,改变span中的内容

优点:
1. 性能提升了。
2. 它使得跨端应用得以实现。React Native

6.redux 阮一峰老师对redux的使用讲解
在这里插入图片描述
这个redux和react交互的过程可以模拟成下面的场景:

一个人(reactComponent)去图书馆借书,需要向图书馆管理员(Store)申请,这个申请的命令(action)传递给了管理员(store),管理员会去他的电脑(reducer)上查看,电脑会接收这个(action以及需要查到这个书的状态previousState),根据借书还是还书的action,然后电脑会将这个书的新的状态(newState),反馈给管理员(store),然后管理员(store)便会将书给这个人(Components),j就是刷新页面,所以说,数据的改变都是通过store来进行改变的,改变的state

二、脚手架工具create-react-app安装

1.使用以下命令进行安装:

npm install -g create-react-app

2.在需要创建项目的位置打开命令行,输入create-react-app + 项目名称的命令

create-react-app todo-list

3.当项目创建完成后,可以进入项目,并启动:

cd todolist
npm start

三、主要目录结构

在这里插入图片描述
主要需要关注的就是index.js,index.html,以及App.js(默认生成的事App.js,根据实际情况可以重命名,我这是TodoList.js)

三、具体实现

需求:输入框中输入待办事件,点击提交在下方列表中显示,点击下方列表中的某个待办事项,可以删除

在这里插入图片描述

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';

ReactDOM.render(<TodoList />, document.getElementById('root'));

这里主要的作用是将TodoList这个组件加载到id是root的标签上

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

主要是存在id="root’的节点

TodoList.js

import React, { Component } from 'react';
import TodoItem from './TodoItem';
import store from './store/index';
import { getInputChangeAction, getAddItemAction, getDelItemAction } from './store/actionCreators';
import './style.css';


class TodoList extends Component {

	constructor(props) {
		super(props);
		this.state = store.getState();
		this.handleInputChange = this.handleInputChange.bind(this);
		this.handleBtnClick = this.handleBtnClick.bind(this);
		this.handleItemDel = this.handleItemDel.bind(this);
		this.handleStoreChange = this.handleStoreChange.bind(this);
		store.subscribe(this.handleStoreChange);
	}


	render() {
		return (
			<div style={{ marginTop: '10px', marginLeft: '10px' }}>
				<div>
					<label htmlFor="insertArea">输入内容</label>
					<input
						id="insertArea"
						className="input"
						value={this.state.inputValue}
						onChange={this.handleInputChange}
					/>
					<button type="primary" onClick={this.handleBtnClick}>
						提交
					</button>
				</div>
				<ul> {this.getTodoItem()}</ul>
			</div>
		)
	}
	getTodoItem() {
		return this.state.list.map((item, index) => {
			return (
				<TodoItem
					index={index}
					content={item}
					delItem={this.handleItemDel}
				/>
			)
		})
	}

	handleStoreChange() {
		this.setState(store.getState());
	}

	handleInputChange(e) {
		//参数是对象
		// this.setState({
		//   inputValue:e.target.value
		// });
		// 参数也可以是箭头函数,异步操作,e获取不到
		const value = e.target.value;
		// this.setState (() => {
		//   return {
		//     inputValue: value
		//   }
		// });
		// ES6语法 return 简写
		// this.setState (() => ({
		//   inputValue: value
		// }));
		const action = getInputChangeAction(value);
		store.dispatch(action);
	}

	handleBtnClick(e) {
		// this.setState(() => ({
		//   list: [...this.state.list,this.state.inputValue],
		//   inputValue: ''
		// }))
		//prevState === this.state
		// this.setState((prevState) => ({
		//   list: [...prevState.list,prevState.inputValue],
		//   inputValue: ''
		// }))
		const action = getAddItemAction();
		store.dispatch(action);
	}

	handleItemDel(index) {
		// const list = [...this.state.list]
		// list.splice(index,1)
		// this.setState(() => ({
		//   list
		// }));
		//此处为了方便将所有操作都放在setState中,这里使用不简写的return返回
		// this.setState((prevState) => {
		//   const list = [...prevState.list]
		//   list.splice(index,1)
		//   return { list }
		// });
		const action = getDelItemAction(index);
		store.dispatch(action);

	}
}
export default TodoList;

其中jsx的函数中包含一个TodoItem的子组件,所以涉及到了父子传值,以及通过redux传值

TodoItem

import React, { Component } from 'react';

class TodoItem extends Component {
    constructor(props) {
        super(props)
        this.handleDel = this.handleDel.bind(this)
    }

    render () {
        const { content } = this.props;
        return (
            <div onClick={this.handleDel} >
              {content}
            </div>
        )
    }

    handleDel () {
      const { delItem, index } = this.props;
      delItem(index)
    }
}

export default TodoItem;

redux相关
在这里插入图片描述
store/index.js

import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

reducer.js

import { ACTION_INPUT_CHANGE, ACTION_ADD_ITEM, ACTION_DEL_ITEM } from './actionType'
const defaultState = {
    inputValue:'',
    list:[]
}
// reducer 可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
    const newState = JSON.parse(JSON.stringify(state));
    if (action.type === ACTION_INPUT_CHANGE) {
       newState.inputValue = action.value;
    }
    if (action.type === ACTION_ADD_ITEM) {
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
    }
    if (action.type === ACTION_DEL_ITEM) {
        newState.list.splice(newState.index,1);
    }
    return newState;
}

actionType.js

const ACTION_INPUT_CHANGE = "action-input-change";
const ACTION_ADD_ITEM = "action-add-item";
const ACTION_DEL_ITEM = "action-del-item";

export {
    ACTION_INPUT_CHANGE,
    ACTION_ADD_ITEM,
    ACTION_DEL_ITEM
}

actionCreators.js

import { ACTION_INPUT_CHANGE, ACTION_ADD_ITEM, ACTION_DEL_ITEM } from './actionType';

const getInputChangeAction = (value) => ({
    type: ACTION_INPUT_CHANGE,
    value
});

const getAddItemAction = () => ({
    type: ACTION_ADD_ITEM
});

const getDelItemAction = (index) => ({
    type: ACTION_DEL_ITEM,
    index
});

export {
    getInputChangeAction,
    getAddItemAction,
    getDelItemAction
}


补充一:使用react-redux编写todoList

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
import { Provider } from 'react-redux';
import store from './store';

const App = (
    <Provider store={store}>
        <TodoList />
    </Provider>
)
ReactDOM.render(App, document.getElementById('root'));

TodoList.js

import React from 'react';
import { connect } from 'react-redux';
import { inputValueAction, addListItemAction, delListItemAction } from './store/actionCreators';

const TodoList = (props) => {
    const { inputValue, changeInputValue, list, handleClickBtn, handleDeleteItem } = props;
    return (
        <div>
            <div>
                <input value={inputValue} onChange={changeInputValue} />
                <button onClick={handleClickBtn}>提交</button>
            </div>
            <ul>
                {
                    list.map((item, index) => {
                        return (
                            <li
                                key={index}
                                index={index}
                                onClick={handleDeleteItem}
                            >
                                {item}
                            </li>
                        )
                    })
                }
            </ul>
        </div>
    )
}


// store.state, props
const mapStateToProps = (state) => {
    return {
        inputValue: state.inputValue,
        list: state.list
    }
}
// store.dispatch, props
const mapDispatchToPros = (dispatch) => {
    return {
        changeInputValue(e) {
            const action = inputValueAction(e.target.value)
            dispatch(action);
        },
        handleClickBtn() {
            const action = addListItemAction()
            dispatch(action)
        },
        handleDeleteItem(e) {
            const action = delListItemAction(e.target.index)
            dispatch(action)
        }
    }
}

export default connect(mapStateToProps, mapDispatchToPros)(TodoList);

补充二:使用redux-thunk中间件来编写TodoList,异步获取数据(数据利用Charles模拟本地数据)

中间件:https://www.zhihu.com/question/19730582

下图的内容,转自上述链接
在这里插入图片描述
redux-thunk中间件是作用于action与store之间的,和react没关系,主要是升级了action,能够使得action不仅可以返回对象,也可以返回函数了,因此主要场景是将复杂的逻辑或者异步操作放在action中操作

store/index.js

import { createStore,applyMiddleware } from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';

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

export default store ;

TodoList.js

import React, { Component } from 'react';
import 'antd/dist/antd.css';
import store from './store';
import { getTodoList, getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators'
import TodoListUI from './TodoListUI';

class TodoList extends Component {

	constructor(props) {
		super(props);
		this.state = store.getState();
		this.handleInputChange = this.handleInputChange.bind(this);
		this.handleStoreChange = this.handleStoreChange.bind(this);
		this.handleBtnClick = this.handleBtnClick.bind(this);
		this.handleItemDelete = this.handleItemDelete.bind(this)
		store.subscribe(this.handleStoreChange);
	}

	render() {
		return (
			<TodoListUI 
				inputValue={this.state.inputValue}
				list={this.state.list}
				handleInputChange={this.handleInputChange}
				handleBtnClick={this.handleBtnClick}
				handleItemDelete={this.handleItemDelete}
			/>
		)
	}

	componentDidMount() {
		const action = getTodoList();
		store.dispatch(action);
	}

	handleInputChange(e) {
		const action = getInputChangeAction(e.target.value);
		store.dispatch(action);
	}

	handleStoreChange() {
		this.setState(store.getState());
	}

	handleBtnClick() {
		const action = getAddItemAction();
		store.dispatch(action);
	}

	handleItemDelete(index) {
		const action = getDeleteItemAction(index);
		store.dispatch(action);
	}

}

export default TodoList;

以上重点就是componentDidMount这个钩子函数,里面的action返回的是一个函数,处理的是一个异步请求,其中可以获取到dispatch

actionCreators.js

import {  INIT_LIST_ACTION } from './actionTypes';
import axios from 'axios';
export const initListAction = (data) => ({
	type: INIT_LIST_ACTION,
	data
});

export const getTodoList = () => {
	return (dispatch) => {
		axios.get('/list.json').then((res) => {
			const data = res.data;
			const action = initListAction(data);
			dispatch(action);
		})
	}
}

TodoListUI .js

import React from 'react';
import { Input, Button, List } from 'antd';

const TodoListUI = (props)=> {
	return (
		<div style={{marginTop: '10px', marginLeft: '10px'}}>
			<div>
				<Input 
					value={props.inputValue} 
					placeholder='todo info' 
					style={{width: '300px', marginRight: '10px'}}
					onChange={props.handleInputChange}
				/>
				<Button type="primary" onClick={props.handleBtnClick}>提交</Button>
			</div>
			<List
				style={{marginTop: '10px', width: '300px'}}
	      bordered
	      dataSource={props.list}
	      renderItem={(item, index) => (<List.Item onClick={() => {props.handleItemDelete(index)}}>{item}</List.Item>)}
	    />
		</div>
	)
}

export default TodoListUI;

TodoListUI是一个容器组件,并且是无状态组件

补充三:使用redux-sage中间件来编写TodoList,异步获取数据(数据利用Charles模拟本地数据)
store/index.js

import { createStore,applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducer';
import mySaga from './sagas';

// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore (
        reducer,
        applyMiddleware(sagaMiddleware)
    );
// then run the saga
sagaMiddleware.run(mySaga);

export default store ;

TodoList.js中的componentDidMount方法

componentDidMount() {
	const action = getInitList();
	store.dispatch(action);
}

store/sagas.js

import { takeEvery, put } from 'redux-saga/effects';
import { GET_INIT_LIST } from './actionTypes';
import { initListAction } from './actionCreators';
import axios from 'axios';

function* getInitList() {
	try {
		const res = yield axios.get('/list.json');
		const action = initListAction(res.data);
		yield put(action);
	}catch(e) {
		console.log('list.json网络请求失败');
	}
}

// generator 函数
function* mySaga() {
  yield takeEvery(GET_INIT_LIST, getInitList);
}

export default mySaga;

store.dispatch(action),不仅是reducer能监听到这个action的分发,saga也可以监听到,takeEvery这个方法就是监听的type是GET_INIT_LIST的action,如果监听到后,就会执行getInitList这个方法,这个方法主要就是异步请求数据,请求完了后通过put(action)分发数据

put函数是用来发送action的 effect,你可以简单的把它理解成为redux框架中的dispatch函数,当put一个action后,reducer中就会计算新的state并返回,注意: put 也是阻塞 effect

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值