项目实战之简书Header组件2

本文详细介绍了一个使用Redux进行状态管理的实战案例,包括如何通过小reducer处理数据展示、页面切换和响应用户交互,如聚焦、鼠标悬停等事件。通过优化代码结构,如使用immutable数据类型、合并多个状态更新和避免不必要的API请求,提高了应用性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

功能:换一换
之前我们让数据全部显示出来 但现在我需要只显示一部分


小reducer.js
import * as constants from './constants';
import { fromJS } from 'immutable';
const defaultState =fromJS({
	focused:false
	list:[],
	page:0,//当前页
	totalPage:1//全部页
	mouseIn:false //是否进入了热门搜索区块
});
export default (state= defaultState ,action) =>{
	switch(action.type){
	case constants.SEARCH_FOCUS :
		return state.set('focused',true);
	case constants.SEARCH_BLUR:
		return state.set('focused',false);
	case constants.CHANGE_LIST :
		return state.set('list',action.data)
					.set('totalPage',action.totalPage);
	case constants.MOUSE_ENTER:
		return state.set('mouseIn',true);
	case constants.MOUSE_LEAVE:
		return state.set('mouseIn',false);
	case constants.CHANGE_PAGE:
		return state.set('page',action.page);
	default:
		return state;
	}
}

然后我们需要对全部页码进行改变
actionCreators.js

import * as constants from './constants';
import axios from 'axios';
import {fromJS} from 'immutable';
export const searchFocus =()=> ({
	type:constants.SEARCH_FOCUS;
})

export const searchBlur =()=> ({
	type:constants.SEARCH_BLUR;
})

export const mouseEnter =()=> ({
	type:constants.MOUSE_ENTER; //constants里同时建立
});

export const mouseLeave =()=> ({
	type:constants.MOUSE_LEAVE; //constants里同时建立
});

export const changePage =(page)=> ({
	type:constants.CHANGE_PAGE; //constants里同时建立
	page
});

const changeList =(data) =>({//所以要把Data变成immutable数组
	type:constants.CHANGE_LIST,
	data:fromJS(data),
	totalPage:Math.ceil(data.length / 10 ) //Math.ceil() === 向上取整
});

export const getList = () => {
	return (dispatch) => {
	axios.get('/api/headerList.json').then((res) =>{
	const data=res.data;
	dispatch(changeList(data.data));
	}).catch(()=>{
		console.log('error');
		})
	}
};

之后我们需要将第一页显示出来
header/index.js
//我们向login里面的store派发 要引入
import { actionCreators as loginActionCreators } from '../../pages/login/store';
class Header extends Component {
	getListArea () {
	    //简化 即可删掉this.props
		const { focused,list,page }=this.props;
		const newList = list.toJS();//转化成普通数组
		const pageList = [];
		
		if(newList.length){//有值才循环
		//加一个循环 初始为第0条到第9条
		for(let i =(page * 10);i< (page+1)*10 ; i++){
			pageList.push(
			<SearchInfoItem key={newList[i]}> {newList[i]} </SearchInfoItem>
			)
		}
		}
		
		if(mouseIn || focused){ //只要一个在就要显示这个区块
		return ( 
		//onmouseenter 事件在鼠标指针移动到元素上时触发
		<SearchInfo 
		onMouseEnter = {this.props.handleMouseEnter}
		onMouseLeave = {this.props.handleMouseLeave}
		>
		 	<SearchInfoTitle>
		 	热门搜索
		      <SearchInfoSwitch onClick={() => handleChangePage(page,totalPage)}>
		      换一批
		      </SearchInfoSwitch>//同时传递过去页码、总页码
		 	</SearchInfoTitle>
		 	<SearchInfoList>
		 	{
		 	{pageList}
		 	})
		 	}
		 	</SearchInfoList>
		 </SearchInfo>) 	}
		else
		{return null;}
	}
	
	render(){
	const { login,logout } =this.props;
		return (
<HeaderWrapper>
	<Logo href='/' />
	<Nav>
		<NavItem className='left active'>首页</NavItem>
		<NavItem className='left'>下载App</NavItem>
		{
			login?<NavItem onClick={logout} className='right'>退出</NavItem>:
			<link to='/login'><NavItem className='right'>登陆</NavItem></link>
		}
		<NavItem className='right'>Aa</NavItem>
		<SearchWrapper>
		<CSSTransition 
			in={this.props.focused} //根据哪个值
			timeout={200}//时长
			classNames="slide">//名字
			
		<NavSearch>
			className={this.props.focused ? 'focused':' '}
			onFocus={this.props.handleInputFocus}
			onBlur={this.props.handleInputBlur}//离开聚焦时
		</NavSearch>
		</CSSTransition>
		<i className={this.props.focusd?'focused iconfont':' '}>
		 &#xe614 
		 </i>
		{this.getListArea()}//调用方法
		</SearchWrapper>
	</Nav>
	<Addition>
		<Button className='reg'>注册</Button>
		<Button className='writting'> 写文章</Button>
	</Addition>
</HeaderWrapper>
)
}

const mapStateToProps =( state )=> {//store里数据state如何映射到props
	return {
	focused:state.getIn('header','focused']),
	list:state.getIn('header','list']),
	page:state.getIn('header','page']),//拿到页码了
	mouseIn:state.getIn('header','mouseIn']),
	totalPage:state.getIn('header','totalPage']),
	login:state.getIn('login','login'])
	}
}

const mapDispatchToProps =( dispatch )=> {//组件改变store里数据
	return {
	handleInputFocus(){
		dispatch(actionCreators.getList());
		dispatch(actionCreators.searchFocus());//action由actioncreators创建
	},
	handleInputBlur(){
		dispatch(actionCreators.searchBlur());
	},
	handleMouseEnter(){
		dispatch(actionCreators.mouseEnter());
	},
	handleMouseLeave(){
		dispatch(actionCreators.mouseLeave());
	}
	handleChangePage(page,totalPage,spin){
		let originAngle=spin.style.transform.replace(/[^0-9]/ig,'');
		//如果里面的不是0-9数字则替换为空
		if(originAngle){
			originAngle = parseInt(originAngle,10);//转化成十进制数字
		}
		else
		{ originAngle = 0; }
		spin.style.transform ='rotate('+ (originAngle+360) + 'deg)';//在。秒内旋转 动画样子始终不会改变 一直是旋转
		if(page<totalPage)
		dispatch(actionCreators.changePage(page + 1));
		else 
		dispatch(actionCreators.changePage( 1));
	},
	logout(){
		dispatch(loginActionCreators.logout())//登出
	}
}
	
}
export default connect (mapStateToProps,mapDispatchToProps)(Header);

我们还发现 热门搜索这一块不是由focuse 聚焦input框实现的
注意发送换一换功能的action时同时还要发送总页码和当前页的相关变量

然后我们来解决key值问题:
在页面渲染时我们代码中key={…}的那个循环也会开始,所以代码中很多变量都是undefined,所以才会出WARNING警告,所以我们要进行判断。

代码优化:
.set是我们改变store的immutable数据的一种方法,但如果需要同时改变很多值,我们不能set很多串,采用另一种方法merge

return state.merge({
	list:action.data,
	totalPage:action.totalPage
});

换一换图标动画效果

先在iconfont中找一个spin
运行之后发现该图标在右下角
因为之前搜索图标已经修改了它的位置靠右下

index.js
之前那个搜索的图标:
<i className={this.props.focusd?'focused iconfont zoom':'iconfont zoom '}>
		 &#xe614 
</i>//末尾加上‘ zoom’

新加的换一换图标:
<SearchInfoTitle>
		 	热门搜索
		      <SearchInfoSwitch onClick={() => handleChangePage(page,totalPage,this.spinIcon)}>
		      //ref可以获取到dom节点,并且可以进一步获得它的css样式
		      <i ref={(icon)=>{this.spinIcon = icon}} className="iconfont spin">&#xe851;</i>
		      换一批
		      </SearchInfoSwitch>//同时传递过去页码、总页码
		 	</SearchInfoTitle>


style.js
export const SearchWrapper = styled.div`
float:left;
position:relative;
.zoom {//修改成zoom
	position :absolute;
	right:5px;
	bottom:5px;
	width:30px;
	line-height:30px;
	border-radius:15px;
	text-align:center;
	}
`

我们开始加动画效果
export const searchInfoSwitch = styled.span`
	...其他略
	.spin{
		display:block;//只有block才能进行旋转rotate
		float:left;
		font-size:12px;
		margin-right:2px;
		transition:all .2s easy-in;//两秒内完成动画
		transform:rotate(0deg);//旋转
		transform-origin:center center;//以中心点旋转 x y 轴
	}
`;
其他地方修改详见上面代码

代码优化之避免无意义的请求发送,提高性能:
我们发现热门搜索这一栏无需点一次请求一次,只需请求一次就足够了

<NavSearch>
			className={this.props.focused ? 'focused':' '}
			onFocus={() => this.props.handleInputFocus(list)}
			//把list传进来
</NavSearch>

handleInputFocus(){
		if(list.size===0){
		dispatch(actionCreators.getList());
		}
		dispatch(actionCreators.searchFocus());//action由actioncreators创建
	},
简洁写法:
handleInputFocus(){
		(list.size===0)&&dispatch(actionCreators.getList());
		dispatch(actionCreators.searchFocus());//action由actioncreators创建
	},

附加:改变鼠标指针样式
我希望在鼠标移到换一批时指针变为手型
找到SearchInfoSwitch

cursor:pointer

我们需要让未登陆时显示“登陆”,登陆后显示”退出“
在mapstatetoprops里建login

### Django 项目实战教程 Django 是一个高级的 Python Web 框架,能够快速开发安全且可维护的网站。对于希望深入学习 Django 的开发者而言,了解如何通过实际案例来掌握其核心概念至关重要。 #### 创建 Django 项目 要开始一个 Django 实战项目,首先需要安装 Django 并创建一个新的项目。可以通过以下命令完成环境准备和项目的初始化[^1]: ```bash pip install django django-admin startproject mysite cd mysite ``` #### 配置路由 在 Django 中,URL 路由用于将 URL 映射到相应的视图函数。这一步骤通常涉及到 `urls.py` 文件的编辑。例如,在主应用目录下设置如下内容可以实现简单的路径映射[^1]: ```python from django.urls import path from . import views urlpatterns = [ path('hello/', views.hello_view, name='hello'), ] ``` #### 开发视图与模板 视图是处理请求并返回响应的核心部分。为了增强用户体验,还可以结合 HTML 和 CSS 使用 Django 提供的强大模板引擎。下面是一个简单视图的例子[^3]: ```python def hello_view(request): context = {'message': 'Hello from Django!'} return render(request, 'index.html', context) ``` 对应的模板文件 (`templates/index.html`) 可能看起来像这样: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Django Template</title> </head> <body> <h1>{{ message }}</h1> </body> </html> ``` #### 数据库操作 Django 自带 ORM(对象关系映射),允许开发者无需编写 SQL 即可轻松管理数据库中的数据。定义模型类即可自动同步至数据库表结构: ```python from django.db import models class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() def __str__(self): return self.title ``` #### 用户认证与权限控制 Django 内建了完善的用户管理系统,支持注册、登录等功能。这些功能可以直接利用内置模块实现而不需要额外编码。 #### 部署上线 当本地测试完成后,最终目标通常是将应用程序部署到生产环境中。常见的做法包括使用 Nginx 或 Apache 结合 Gunicorn 来运行服务[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七灵微

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值