目录
1.条件渲染
方式1:利用函数方法输出组件
renderButton(){
const isLoggedIn = this.state.isLoggedIn;
if(isLoggedIn)
{
return (<LogoutButton onClick={this.handleLogoutClick} />);
}
else
{
return (<LoginButton onClick={this.handleLoginClick} />);
}
}
render() {
return (
<div>
<Greeting />
{this.renderButton()}
</div>
);
}
方式2:用变量来储存元素
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
2.列表循环
<ul>
{props.posts.map((item) =>
return (
<li key={item.id}>
{item.title}
</li>
)
)}
</ul>
3.事件
- 定义事件时,要对this进行绑定,建议写在constructor中,写在事件绑定后时,那么每一次事件执行都要进行一次this绑定
- 关于event参数
- event是SyntheticEvent,但模拟DOM事件所有能力
- 可以通过event.nativeEvent获取原生对象
- 所有的事件,都被挂在到document上,vue中事件绑定和触发都在一个元素上
- vue中的事件就是原生的事件
clickHandler(){
// 这里需要对this进行绑定,因为这里的this默认是undefined
}
clickHandler = () => {
// 这里不需要对this进行绑定
}
// 第一种:event会追加在最后一个参数
<div onClick={this.clickHandler.bind(this, id, title)}></div>
clickHandler(id, title, event){
// event会追加在最后一个参数
}
// 第二种:事件对象作为第二个参数传递
<button onClick={e => this.handleClick('zhangjie', e)}>点击事件</button>
handleClick = (...args) => {
console.log(args);
}
// 第三种:返回一个函数,事件对象在返回的函数中
<button onClick={this.handleClick('zj')}>点击事件</button>
handleClick = (name) => {
return (e) => {
console.log(e, name);
}
}
4.表单
- 受控组件
- 即表单中的值受state中的值控制
- 例如将input的值与state值关联起来,input的值可以通过state中的值来进行控制
- 如果需要实现双向数据绑定,那么可以监听change事件
<textarea value={this.state.text}></textarea>
<select value={this.state.city} onChange={this.onSelectCHange}>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
</select>
<input type="checkbox" checked={this.state.flag} onChange={this.checkboxChange} />
<input type="radio" name="gender" value="male" checked={this.state.gender === 'male'} onChange={this.radioChange} />
<input type="radio" name="gender" value="female" checked={this.state.gender === 'female'} onChange={this.radioChange} />
5.父子组件通信
- 父组件向子组件传递数据,子组件通过props进行接收
<SubComponent title="传递给子组件的数据"></SubComponent>
// 子组件通过props进行接收
function SubComponent(props){
return (
<div>
{props.title}
</div>
)
}
- 子组件向父组件传递数据,父组件向子组件传递props,传递的props为父组件自身函数,子组件调用该函数,将信息作为函数的参数传递给父组件的作用域中
// 父组件
class App extends React.Component {
render(){
return (
<div>
<SubComponent callback={this.callback.bind(this)}></SubComponent>
</div>
);
}
callback(msg){
console.log(msg)
}
}
// 子组件
class SubComponent extends React.Component{
render(){
return (
<div>
<button onClick={this.sendDataToParent.bind(this, '我们通信吧')}>点击我</button>
</div>
)
}
sendDataToParent(msg){
this.props.callback(msg);
}
}
- 跨级组件通信context,例如将公共信息(语言,主题)传递给每个组件,此时用props太繁琐,用redux小题大,那么可以使用context
const ThemeContext = React.createContext({
theme:'light'
});
class App extends React.Component{
constructor(props){
super(props);
this.state = {
theme: 'light'
}
}
render(){
return (
<ThemeContext.Provider value={this.state}>
<Toolbar></Toolbar>
<button onClick={this.changeTheme}>change theme</button>
</ThemeContext.Provider>
)
}
changeTheme = () => {
this.setState({
theme: this.state.theme === 'light' ? 'dark' : 'light'
})
}
}
class Toolbar extends React.Component{
render(){
return (
<div>
<ThemeButton></ThemeButton>
</div>
)
}
}
class ThemeButton extends React.Component{
render (){
return (
<ThemeContext.Consumer>
{
({theme}) =>
<p>link's theme is {theme}</p>
}
</ThemeContext.Consumer>
)
}
}
6.setState不可变值
- state中的值不可以直接进行修改, 需要通过setState进行修改
const list5Copy = this.state.list5.slice();
list5Copy.splice(2, 0, 'a');
this.setState({
list1: this.state.list1.concat(100), // 追加
list2: [...this.state.list2, 100], // 追加
list3: this.state.list3.slice(0,3), // 截取
list4: this.state.list4.filter(item => item>100), // 筛选
list5: list5Copy
})
this.setState({
obj1: Object.assign({}, this.state.obj1, {'a': 100}),
obj2: {...this.state.obj2, 'a': 100}
})
-
setState可能是异步操作
// 直接使用是异步
this.setState({
count: this.state.count++
}, () => {
console.log('count by callback', this.state.count);
})
// setTimeout中setState是同步的
setTimeout(() => {
this.setState({
count: this.state.count++
})
console.log(this.state.count);
}, 0)
// 自己定义的DOM事件,setState是同步的
const bodyClickHandler = () => {
this.setState({
count: this.state.count++
})
console.log(this.state.count)
}
componentDIdMount(){
docuement.body.addEventLisetener('click', this.bodyClickHandler)
}
componentWillUnmount(){
// 及时销毁DOM事件及timer
docuement.body.removeEventLisetener('click', this.bodyClickHandler)
}
-
可能会被合并
// 以下会进行合并,及只执行一次,即最后count为1
// 类似于Object.assign({count:1},{count:1})
this.setState({
count: this.state.count++
})
this.setState({
count: this.state.count++
})
this.setState({
count: this.state.count++
})
// 传入参数不会被合并,即最后count为3
// 因为传入的是函数,函数本身是不会被合并的
this.setState((prevState, props) => {
return {
count: prevState.count+1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count+1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count+1
}
})
7.生命周期
- 可参考react生命周期
8.函数组件
- 当组件只接收props,可以使用函数组件
- 纯函数:输入props,输出JSX
- 没有实例,没有生命周期,没有state
class List extends React.Component{
constructor(props){
super(props)
}
render(){
const {list} = this.props;
return (
<ul>
list.map((item) =>
return (
<li key={item.id}>
{item.title}
</li>
)
)
</ul>
)
}
}
// 函数组件
function List (props){
const {list} = this.props;
return (
<ul>
list.map((item) =>
return (
<li key={item.id}>
{item.title}
</li>
)
)
</ul>
)
}
9.非受控组件
- 一般用于需要操作DOM,不能通过state来完成操作时使用,例如文件上传,上传之后获取文件内容。
- defaultValue:在非受控组件中,如果希望react能赋予一个初始值,但是不进行后续的更新,此时可以指定一个defaultValue属性
<input type="checkbox">
和<input type="radio">
支持defaultChecked
,<select>
和<textarea>
支持defaultValue
class App extends React.Component{
constructor(props){
super(props);
this.state = {
name: 'name'
}
this.nameInputRef = Reatc.createRef();
}
render(){
return (
<input defaultValue={this.state.name} ref={this.nameInputRef} />
<button onClick={this.alertName}></button>
)
}
alertName = () => {
const ele = this.nameInputRef.current;
// 不是state中值,是input中的值,即input中的值不受state中控制,input中输入的值和state并没有关系
alert(ele.value);
}
}
-
如下实现在进行提交时获取文件信息的demo
class FileInput extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.fileInput = React.createRef();
}
handleSubmit(event) {
event.preventDefault();
alert(
`Selected file - ${this.fileInput.current.files[0].name}`
);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Upload file:
<input type="file" ref={this.fileInput} />
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}
}
10.portals
- 组件默认会按照既定层次嵌套渲染
- 如果父组件overflow:hidden;父组件z-index太小,且某个组件需要实现fixed放在body第一层,那么可以使用portal实现使子组件逃离父组件进行渲染
// 父组件
render(){
return (
<portalsDemo>
Model Content
</portalsDemo>
)
}
// 子组件
render(){
// this.props.children类似于vue中的插槽,可以获取到组件引用时嵌套的内容
return ReactDOM.createPortal(
<div className="modal">{this.props.children}</div>,
document.body // DOM节点,表示该组件会挂载body上
)
}
11.异步组件
- 组件比较大,路由需要懒加载,就需要使用异步组件
- React.lazy与React.suspense
const ContextDemo = React.lazy(() => import('./ContextDemo'))
class App extends React.Component{
render(){
return (
<div>
<p>引入一个动态组件</p>
<br/>
<React.Suspense fallback={<div>loading...</div>}>
<ContextDemo />
</React.Suspense>
</div>
)
}
}