转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 需求
❓查看"简书"官网,并实现需求:
- 当点击"回到顶部"按钮时,页面立刻回到顶部;
- "回到顶部"按钮一开始是"隐藏"状态,当页面向下滑动一定值的时候,"回到顶部"按钮出现(反之亦然)。
✔️需求分析:
- 单就"回到顶部"这个功能而言,一行代码就可以搞定------调用
window.scrollTo(0, 0)
方法; - 但"回到顶部"在适当的时机"隐藏"和"显示"则需要走 React-redux"数据"改变的流程,稍微麻烦点,但还是"套路",所以不可怕。
2 编写"回到顶部"逻辑代码
🔗前置知识: 《JavaScript 基础------浏览器提供的对象:① BOM》------掌握"常用的 window 对象属性"; 《JavaScript 基础------浏览器提供的对象:② DOM》------掌握" document.documentElement.scrollTop
的用法"; 《JavaScript 基础------JS 事件:③ 常见事件使用》------掌握"window 事件"
1️⃣打开 home 目录下的 index.js
文件,我们先实现简单的"回到顶部"功能:
jsx
import React, {Component} from "react";
import Content from "./components/Content";
import Label from "./components/Label";
import Panels from "./components/Panels";
import Download from "./components/Download";
import {
Section,
Aside,
Main,
ToTop
} from "./style.js";
import { connect } from "react-redux";
import {actionCreators} from "./store";
class Home extends Component {
// 1️⃣-②:我们直接将 handleScrollTop 事件方法写在这里;
handleScrollTop() {
window.scrollTo(0, 0);
}
render() {
return(
<div>
<Section className="layout clearfix">
<Aside>
<Panels />
<Download />
</Aside>
<Main>
<img className="banner-img" src="https://qdywxs.github.io/jianshu-images/carousel01.jpg" alt="" />
<Label />
<Content />
</Main>
</Section>
{/* ❗️1️⃣-①:给 ToTop 样式组件绑定一个 click 点击事件; */}
<ToTop onClick={this.handleScrollTop}>
<span className="up">^</span>
<span className="tooltip">回到顶部</span>
</ToTop>
</div>
)
}
componentDidMount() {
this.props.changeHomeData();
}
}
const mapDispatchToProps = (dispatch) => {
return {
changeHomeData() {
const action = actionCreators.getHomeInfo();
dispatch(action)
},
}
}
export default connect(null, mapDispatchToProps)(Home);
返回页面查看(成功回到顶部):
2️⃣继续去实现"在适当时机"显示和隐藏"回到顶部"按钮,打开 home 目录下 store 中的 reducer.js
文件,在其中定义一个变量 showToTop
,用其来保存"样式组件 ToTop
"的"显示"和"隐藏":
javascript
import {fromJS} from "immutable";
import {INIT_HOME_DATA, ADD_HOME_DATA} from "./actionTypes";
const defaultState = fromJS({
labelList: [],
articleList: [],
panelsList: [],
articlePage: 1,
// ❗️2️⃣-①:定义一个变量,用以保存 ToTop 的"显示"和"隐藏";
showToTop: false // ❗️❗️❗️初始为"不显示"!
})
export default (state=defaultState, action) => {
if(action.type === INIT_HOME_DATA) {
return state.merge({
labelList: action.labelList,
articleList: action.articleList,
panelsList: action.panelsList
})
}
if(action.type === ADD_HOME_DATA) {
return state.merge({
"articleList": state.get("articleList").concat(action.moreArticleList),
"articlePage": action.nextPage
})
}
return state;
}
2️⃣-②:返回 home 目录下的 index.js
文件;
javascript
import React, {Component} from "react";
import Content from "./components/Content";
import Label from "./components/Label";
import Panels from "./components/Panels";
import Download from "./components/Download";
import {
Section,
Aside,
Main,
ToTop
} from "./style.js";
import { connect } from "react-redux";
import {actionCreators} from "./store";
class Home extends Component {
handleScrollTop() {
window.scrollTo(0, 0);
}
render() {
return(
<div>
<Section className="layout clearfix">
<Aside>
<Panels />
<Download />
</Aside>
<Main>
<img className="banner-img" src="https://qdywxs.github.io/jianshu-images/carousel01.jpg" alt="" />
<Label />
<Content />
</Main>
</Section>
{/* 2️⃣-⑤:然后,我们在这里来编写"显示"和"隐藏"的逻辑------用"三元运算符"来作判断; */}
{this.props.showToTop ?
<ToTop onClick={this.handleScrollTop}>
<span className="up">^</span>
<span className="tooltip">回到顶部</span>
</ToTop>
:null
}
</div>
)
}
componentDidMount() {
this.props.changeHomeData();
}
}
// 2️⃣-④:定义 mapStateToProps:
const mapStateToProps = (state) => ({
showToTop: state.getIn(["home", "showToTop"])
})
const mapDispatchToProps = (dispatch) => {
return {
changeHomeData() {
const action = actionCreators.getHomeInfo();
dispatch(action)
},
}
}
/*
2️⃣-③:既然 reducer 中新增了"数据",且本组件会用到这个"数据",
故,给 connect 传递另一个参数 mapStateToProps,进而去获取到这个"数据";
注释掉下面这行代码,重新编写~
export default connect(null, mapDispatchToProps)(Home);
*/
export default connect(mapStateToProps, mapDispatchToProps)(Home)
返回页面查看(由于初始为 false
,所以一开始"返回顶部"是不显示的。但,当我们将 showToTop
的值改为 true
时,"返回顶部"又显示出来了):
✔️由上边的视频可见,控制"返回顶部"的"显示"和"隐藏",其实就是控制 showToTop
这个"数据"的值( true
/ false
)!
3️⃣返回 home 目录下的 index.js
文件:
jsx
import React, {Component} from "react";
import Content from "./components/Content";
import Label from "./components/Label";
import Panels from "./components/Panels";
import Download from "./components/Download";
import {
Section,
Aside,
Main,
ToTop
} from "./style.js";
import { connect } from "react-redux";
import {actionCreators} from "./store";
class Home extends Component {
handleScrollTop() {
window.scrollTo(0, 0);
}
render() {
return(
<div>
<Section className="layout clearfix">
<Aside>
<Panels />
<Download />
</Aside>
<Main>
<img className="banner-img" src="https://qdywxs.github.io/jianshu-images/carousel01.jpg" alt="" />
<Label />
<Content />
</Main>
</Section>
{this.props.showToTop ?
<ToTop onClick={this.handleScrollTop}>
<span className="up">^</span>
<span className="tooltip">回到顶部</span>
</ToTop>
:null
}
</div>
)
}
componentDidMount() {
this.props.changeHomeData();
// ❗️3️⃣-①:我们在生命周期函数 componentDidMount 里绑定一些事件;
this.bindEvent();
}
// 3️⃣-②:具体的事件方法写在这里;
bindEvent() {
// ❗️监听 window 的 scroll 事件!
// ❓3️⃣-③:但具体怎么调用呢?
window.addEventListener("scroll", this.props.changeShowToTop) /*
3️⃣-⑤:因此这里
通过 this.props 来
调用 changeToTopShow
方法;
*/
}
}
const mapStateToProps = (state) => ({
showToTop: state.getIn(["home", "showToTop"])
})
const mapDispatchToProps = (dispatch) => ({
changeHomeData() {
const action = actionCreators.getHomeInfo();
dispatch(action)
},
// 3️⃣-④:定义 scroll 这个"用户行为"会被传递给 reducer;
changeShowToTop() {
/*
3️⃣-⑥:用 document.documentElement.scrollTo 来作条件判断,
当滚动到一定量的时候,进行"数据"的改变;
*/
if(document.documentElement.scrollTop > 120) {
const action = actionCreators.changeShowToTopAction(true); // ❗️传递一个参数 true!
dispatch(action)
}else {
const action = actionCreators.changeShowToTopAction(false); // ❗️传递一个参数 false!
dispatch(action)
}
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home)
3️⃣-⑦:请记得去 home 目录下 store 中的 actionTypes.js
和 actionCreators.js
文件中分别定义"常量"和 action;
actionTypes.js
javascript
export const INIT_HOME_DATA = "init_home_data";
export const ADD_HOME_DATA ="add_home_data";
// ❗️定义好常量~
export const CHANGE_SHOW_TO_TOP="change_show_to_top";
actionCreators.js
javascript
import axios from "axios";
// ❗️❗️❗️先引入"常量"!
import {INIT_HOME_DATA, ADD_HOME_DATA, CHANGE_SHOW_TO_TOP} from "./actionTypes";
import {fromJS} from "immutable";
const initHomeData = (result) => ({
type: INIT_HOME_DATA,
labelList: fromJS(result.labelList),
articleList: fromJS(result.articleList),
panelsList: fromJS(result.panelsList)
});
export const getHomeInfo = () => {
return(dispatch) => {
axios.get("/api/homeData.json")
.then((res) => {
const result = res.data.data;
const action = initHomeData(result);
dispatch(action);
})
.catch(() => {alert("error")})
}
}
const addHomeData = (result, nextPage) => ({
type: ADD_HOME_DATA,
moreArticleList: fromJS(result.moreArticleList),
nextPage
})
export const getMoreList = (page) => {
return(dispatch) => {
axios.get("/api/homeList.json?page=" + page)
.then((res) => {
const result = res.data.data;
const action = addHomeData(result, page + 1);
dispatch(action);
})
.catch(() => {alert("error")})
}
}
// 3️⃣-⑧:定义 action;
export const changeShowToTopAction = (show) => ({
type: CHANGE_SHOW_TO_TOP,
show
})
3️⃣-⑨:打开 home 目录下 store 中的 reducer.js
文件,编写修改数据的逻辑;
javascript
import {fromJS} from "immutable";
// ❗️❗️❗️先引入"常量"!
import {INIT_HOME_DATA, ADD_HOME_DATA, CHANGE_SHOW_TO_TOP} from "./actionTypes";
const defaultState = fromJS({
labelList: [],
articleList: [],
panelsList: [],
articlePage: 1,
showToTop: false // ❗️❗️❗️初始为"不显示"!
})
export default (state=defaultState, action) => {
if(action.type === INIT_HOME_DATA) {
return state.merge({
labelList: action.labelList,
articleList: action.articleList,
panelsList: action.panelsList
})
};
if(action.type === ADD_HOME_DATA) {
return state.merge({
"articleList": state.get("articleList").concat(action.moreArticleList),
"articlePage": action.nextPage
})
};
// 3️⃣-⑩:当reducer 接收到 action 后,需要告诉 store 怎样"改变数据";
if(action.type === CHANGE_SHOW_TO_TOP) {
return state.set("showToTop", action.show);
}
return state;
};
返回页面查看(成功实现在"适当的时机"去"显示"和"隐藏"):
3 移除 scroll
事件
❗️以上我们虽然实现了本篇一开始提的"需求",但还有一个点一定得注意:
4️⃣回到 home 目录下的 index.js
文件:
jsx
import React, {Component} from "react";
import Content from "./components/Content";
import Label from "./components/Label";
import Panels from "./components/Panels";
import Download from "./components/Download";
import {
Section,
Aside,
Main,
ToTop
} from "./style.js";
import { connect } from "react-redux";
import {actionCreators} from "./store";
class Home extends Component {
handleScrollTop() {
window.scrollTo(0, 0);
}
render() {
return(
<div>
<Section className="layout clearfix">
<Aside>
<Panels />
<Download />
</Aside>
<Main>
<img className="banner-img" src="https://qdywxs.github.io/jianshu-images/carousel01.jpg" alt="" />
<Label />
<Content />
</Main>
</Section>
{this.props.showToTop ?
<ToTop onClick={this.handleScrollTop}>
<span className="up">^</span>
<span className="tooltip">回到顶部</span>
</ToTop>
:null
}
</div>
)
}
componentDidMount() {
this.props.changeHomeData();
// ❗️4️⃣-①:当组件"挂载"好后,我们在这里绑定了一些事件;
this.bindEvent();
}
// ❗️4️⃣-③:所以,我们在生命周期函数 componentWillUnmount 中"解绑"scroll 事件!
componentWillUnmount() {
window.removeEventListener("scroll", this.props.changeShowToTop)
}
bindEvent() {
/*
❗️❗️❗️4️⃣-②:且,这些"事件"是绑定在"全局"的 window 对象上的!故,我们需要做的是------
当"组件"被从页面上移除的时候,我们一定得把 scroll 事件从 window 上"解绑"。
否则,这个"组件"的事件就会影响其他的"组件"!
*/
window.addEventListener("scroll", this.props.changeShowToTop)
}
}
const mapStateToProps = (state) => ({
showToTop: state.getIn(["home", "showToTop"])
})
const mapDispatchToProps = (dispatch) => ({
changeHomeData() {
const action = actionCreators.getHomeInfo();
dispatch(action)
},
changeShowToTop() {
if(document.documentElement.scrollTop > 120) {
const action = actionCreators.changeShowToTopAction(true);
dispatch(action)
}else {
const action = actionCreators.changeShowToTopAction(false);
dispatch(action)
}
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home)
返回页面查看,无任何异常,成功实现:
下一篇,我们开始编写"详情页"的代码,我们再去好好地走走"流程",用用"套路"。孰能生巧,多写几次,你一定能熟练掌握 React 的使用。
祝好,qdywxs ♥ you!