React重要学习方式:官方文档
React:
(1)声明式开发;(原生js、jQuery命令式开发)
(2)可以与其它框架并存;(比如Vue、jQuery等,每个只负责自己的组件部分)
(3)组件化;(组件首字母大写)
(4)单向数据流;(父可以传给子,子不能修改父的state)
(5)视图层框架----解决数据和页面渲染问题;(大型项目需配合数据层框架flux、redux等----解决组件之间复杂传值问题)
(6)函数式编程。(易维护、有益于前端自动化测试)
1.React 向事件处理程序传递参数的两种方式:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述两种方式是等价的,分别通过 arrow functions 和 Function.prototype.bind 来为特定事件类型添加事件处理程序。
上面两个例子中,参数 e 作为 React 事件对象将会被作为第二个参数进行传递。通过箭头函数的方式,事件对象必须显式的进行传递,但是通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。
值得注意的是,通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面。
2.dangerouslySetInnerHTML:使item中的HTML标签起作用,而不是直接显示出来(即不被转义)
3.<label htmlFor="insertArea">输入内容</label>:lable的作用:扩大点击的区域,通过htmlFor将光标聚焦到input
4.Fragment占位符:return下只能有一个标签比如div,若要不想显示最外层div,用Fragment替代它
5.事件触发的方法需要用bind绑定this,且方法后不加();直接需要执行的方法不用bind绑定this,方法后加()。
bind绑定必须在使用函数之前。
<button onClick={this.handleButtonClick}>提交</button>
<ul>{this.getTodoItem()}</ul>
getTodoItem(){
return(
this.state.list.map((item, index) => {
return (
<div>
<TodoItem content={item} index={index} deleteItem={this.handleItemDelete}/>
</div>
)
})
)
}
handleButtonClick() {
this.setState({
inputValue: '',
list: [...this.state.list, this.state.inputValue]
})
}
6.在jsx中做循环时(比如map循环返回新数组、子组件时),对循环中的每一项(最外层的)都要赋予一个唯一的key值。
不要用index作为key值,因为index有可能会改变(比如删除数组中的一个元素时)。
getTodoItem() {
return (
this.state.list.map((item, index) => {
return (
<TodoItem key={index} content={item} index={index} deleteItem={this.handleItemDelete}/>
)
})
)
}
***组件拆分和组件之间的传值
(1)父子组件之间如何传递参数、方法?
父组件中通过设置子组件的属性传递参数、方法;子组件通过props接受参数、方法。
(2)子组件如何更改父组件中的state?
在父子组件结构中,采用状态提升,故父组件中的state由父组件的方法更改;若子组件想改变state,通过方法下放。
(3)子组件如何调用父组件中的方法?----方法下放
1.父组件将方法通过子组件属性传递给子组件(传递前,须将方法的this指向绑定到父组件上)。
2.子组件通过props调用该方法(该方法可以实现对父state的改变,即实现子组件改变父组件中的state)(调用时可向该方法传递想传递的参数)。
实例分析:TodoList
(1)index.js
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
ReactDOM.render(<TodoList />, document.getElementById('root'));
(2.1)TodoList.js(没采用父子组件结构)
import React, {Component, Fragment} from 'react';
import './style.css';
import TodoItem from './TodoItem';
class TodoList extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: '',
list: []
}
this.handleItemDelete=this.handleItemDelete.bind(this);
}
render() {
return (
// Fragment占位符,return下只能有一个标签比如div,若要不想显示最外层div,用Fragment替代它
<Fragment>
<div>
<label htmlFor="insertArea">输入内容</label>
{/*lable的作用:扩大点击的区域,通过htmlFor将光标聚焦到input*/}
<input id={'insertArea'} className={'input'} value={this.state.inputValue}
onChange={(e) => this.handleInputChange(e)}/>
{/*<input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}/>*/}
<button onClick={() => this.handleButtonClick()}>提交</button>
</div>
<ul>
{ //根据list中内容将其渲染到ul中
// 经过map()方法后将原list: ['React','vue']数组变为['<li>React</li>','<li>vue</li>']
this.state.list.map((item, index) => {
return (
<li key={index}
onClick={(e) => this.handleItemDelete(index, e)}
dangerouslySetInnerHTML={{__html: item}}
//dangerouslySetInnerHTML:使item中的HTML标签起作用,而不是直接显示出来
>
</li>
)
})
}
</ul>
</Fragment>
)
}
handleInputChange(e) {
// this.setState({
// inputValue: e.target.value
// })
this.setState(()=>{
return{
inputValue: e.target.value
}
})
}
handleButtonClick() {
// this.setState({
// inputValue: '',
// list: [...this.state.list, this.state.inputValue]
// })
this.setState(()=>{
return{
inputValue: '',
list: [...this.state.list, this.state.inputValue]
}
})
}
handleItemDelete(index) {
const list = [...this.state.list];
list.splice(index, 1);
// this.setState({
// list: list
// })
this.setState(()=>{
return{
list: list
}
})
}
}
export default TodoList;
(2.2)TodoList.js(采用父子组件结构----父组件)
import React, {Component, Fragment} from 'react';
import './style.css';
import TodoItem from './TodoItem';
class TodoList extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: '',
list: []
}
this.handleItemDelete=this.handleItemDelete.bind(this);
}
render() {
return (
// Fragment占位符,return下只能有一个标签比如div,若要不想显示最外层div,用Fragment替代它
<Fragment>
<div>
<label htmlFor="insertArea">输入内容</label>
{/*lable的作用:扩大点击的区域,通过htmlFor将光标聚焦到input*/}
<input id={'insertArea'} className={'input'} value={this.state.inputValue}
onChange={(e) => this.handleInputChange(e)}/>
{/*<input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}/>*/}
<button onClick={() => this.handleButtonClick()}>提交</button>
</div>
<ul>
{ //根据list中内容将其渲染到ul中
// 经过map()方法后将原list: ['React','vue']数组变为['<li>React</li>','<li>vue</li>']
this.state.list.map((item, index) => {
return (
<TodoItem content={item} index={index} deleteItem={this.handleItemDelete}/>
//由于单向数据流,这里不能直接将state中的list传给子组件,子组件不能直接修改state。
)
})
}
</ul>
</Fragment>
)
}
handleInputChange(e) {
this.setState({
inputValue: e.target.value
})
}
handleButtonClick() {
this.setState({
inputValue: '',
list: [...this.state.list, this.state.inputValue]
})
}
handleItemDelete(index) {
const list = [...this.state.list];
list.splice(index, 1);
this.setState({
list: list
})
}
}
export default TodoList;
(3)TodoItem.js(采用父子组件结构----子组件)
import React from 'react';
class TodoItem extends React.Component{
constructor(props){
super(props);
this.handleClick=this.handleClick.bind(this);
}
shouldComponentUpdate(nextProps, nextState){
if(nextProps.content !== this.props.content){
return true;
}else{
return false;
}
}
render() {
return(
<div onClick={this.handleClick}>{this.props.content}</div>
)
}
handleClick(){
this.props.deleteItem(this.props.index);
}
}
export default TodoItem;
****TodoList优化后的代码:
(1)TodoList.js
import React, {Component, Fragment} from 'react';
import TodoItem from './TodoItem';
import './style.css';
class TodoList extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: '',
list: []
}
this.handleInputChange = this.handleInputChange.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
this.handleItemDelete = this.handleItemDelete.bind(this);
}
render() {
return (
<Fragment>
<div>
<label htmlFor="insertArea">输入内容</label>
<input id={'insertArea'}
className={'input'}
value={this.state.inputValue}
onChange={this.handleInputChange}/>
<button onClick={this.handleButtonClick}>提交</button>
{/*事件触发的方法需要用bind绑定this; 且方法后不加()*/}
</div>
<ul>{this.getTodoItem()}</ul>
{/*直接需要执行的方法不用bind绑定this; 方法后加()*/}
</Fragment>
)
}
getTodoItem() {
return (
this.state.list.map((item, index) => {
return (
<TodoItem key={index} content={item} index={index} deleteItem={this.handleItemDelete}/>
)
})
)
}
handleInputChange(e) {
const value = e.target.value;
this.setState(() => ({
inputValue: value
})
)
}
handleButtonClick() {
this.setState((prevState) => ({
inputValue: '',
list: [...prevState.list, prevState.inputValue]
})
)
}
handleItemDelete(index) {
this.setState((prevState) =>{
const list = [...prevState.list];
list.splice(index, 1);
return {list: list}
}
)
}
}
export default TodoList;
(2)TodoItem.js
import React from 'react';
class TodoItem extends React.Component{
constructor(props){
super(props);
this.handleClick=this.handleClick.bind(this);
}
render() {
const {content}=this.props; //ES6的解构赋值
return(
<div onClick={this.handleClick}>{content}</div>
)
}
handleClick(){
const {deleteItem, index}=this.props; //ES6的解构赋值
deleteItem(index);
}
}
export default TodoItem;