项目实战
1.脚手架生成项目
create-react-app jianshu
2.styled-components的使用
- 1.安装styled-components包
yarn add styled-components
- 2.创建style.js文件,代码如下
import { createGlobalStyle } from "styled-components";
//导出全局样式
export const GlobalStyle = createGlobalStyle `
//此处是全局样式表,可以把reset.css放在这里
html, body, div, span{...}
`
- 3.在index.js中引入,代码如下
import React from 'react';
import ReactDOM from 'react-dom';
//引入全局样式
import {GlobalStyle} from "./css/style.js";
import App from './App';
ReactDOM.render(
<React.StrictMode>
/*全局样式*/
<GlobalStyle />
<App />
</React.StrictMode>,
document.getElementById('root')
);
3.在组建中使用styled-components
- header组件的编写
-
header组件目录结构如下
image.png
- header (文件夹)
- index.js (header组件的代码)
- style.js (header组件的样式)
- header (文件夹)
- index.js代码如下
-
import react,{Component} from "react";
import {HeaderWrapper,NavLogo,Nav,NavLeft,NavRight,HeaderLink,LinkItem} from "./style"
class header extends Component{
render(){
return (
<HeaderWrapper>
<NavLogo href="/"></NavLogo>
<Nav>
<NavLeft>
<p className="p1">首页</p>
<p className="p2">下载APP</p>
<input type="text" placeholder="搜索"/>
</NavLeft>
<NavRight>
<p>Aa</p>
<a href="">登录</a>
</NavRight>
</Nav>
<HeaderLink>
<LinkItem>注册</LinkItem>
<LinkItem className="link2">写文章</LinkItem>
</HeaderLink>
</HeaderWrapper>
)
}
}
export default header;
style.js代码如下
import styled from "styled-components";
//引入图片
import logoImg from "../../asstes/img/nav-logo.png";
//HeaderWrapper会被解析成div元素 模板字符串中写样式,把HeaderWrapper暴露出去,在组件中使用就不会产生样式冲突问题了。
export const HeaderWrapper = styled.div`
display: flex;
height: 58px;
padding: 0 39px;
border-bottom:1px solid #eee;
`;
//NavLogo会被解析成a标签 注意图片要用import的方式
export const NavLogo = styled.a`
background: url("${logoImg}") no-repeat;
`;
//NavLeft有子元素,子元素样式的写法
export const NavLeft = styled.div`
NavLeft的样式
.p1{
p1的样式
}
.p2{
p2的样式
}
input{
input的样式
}
`;
//其他代码省略....
4.使用redux改造代码
import { Component } from "react";
import {connect} from "react-redux";
import {changeFocusStatus} from "../../store/actionCreator";
class header extends Component {
constructor(props){
super(props);
this.state = {
focus:false
}
}
render() {
const {props} = this;
return (
<HeaderWrapper>
/*重点代码 其他代码省略*/
<div className="search" className={props.foucsed==true?"search focus":"search"} >
<input type="text" placeholder="搜索" onFocus={()=>{props.changeFoucs(true)}} onBlur={()=>{props.changeFoucs(false)}} />
<span className="iconfont icon-fangdajing"></span>
</div>
</HeaderWrapper>
);
}
}
const mapStateToProps = (state)=>{
return {
foucsed:state.foucsed
}
}
const mapActionToProps = (dispatch)=>{
return {
changeFoucs(value){
const action = changeFocusStatus(value);
dispatch(action)
}
}
}
export default connect(mapStateToProps,mapActionToProps)(header);
5.拆分reducer.js,然后使用combineReducers进行组合,类似于vue的store拆分
- reducer.js代码
import { combineReducers } from "redux";
import { reducer as HeaderReducer } from "../common/header/store";
const reducer = combineReducers({
header:HeaderReducer
});
export default reducer;
-
在header组件中创建store文件夹,下面存放actionCreators、reducer.js、actionTypes(更改文件名字为constant.js),之后在index.js中把三个文件引入统一对外暴露。目录结构如下图所示:
image.png
- index.js代码
import reducer from "./reduce";
import * as constant from "./constant";
import * as actionCreators from "./actionCreators";
export { reducer , constant , actionCreators }
- reducer.js代码
import {CHANGE_FOUCS_STATUS} from "./constant";
const defaultStore = {
foucsed:false
};
export default (store = defaultStore, action) => {
if(action.type==CHANGE_FOUCS_STATUS){
const newStore = JSON.parse(JSON.stringify(store));
newStore.foucsed = action.value;
return newStore;
}
return store;
};
constant.js(也就是actionTypes.js)代码 constant是常量的意思
export const CHANGE_FOUCS_STATUS = "change_foucs_status";
actionCreators.js代码
import { CHANGE_FOUCS_STATUS } from "./constant";
export const changeFocusStatus = (value) => {
return {
type: CHANGE_FOUCS_STATUS,
value,
};
};
- 最后修改header组件index.js代码引入constant.js(也就是actionTypes.js)的方式
//其他代码省略
import { actionCreators } from "./store";
const mapActionToProps = (dispatch)=>{
return {
changeFoucs(value){
const action = actionCreators.changeFocusStatus(value);
dispatch(action)
}
}
}
5.使用immutable包保证state中的数据不被修改(防止写代码误操作)
- 默认的数据在reducer.js中,改造reducer.js代码
import { CHANGE_FOUCS_STATUS } from "./constant";
//引入immutable库,mutable"可变的"的意思,immutable"不可变的"意思
import { fromJS } from "immutable";
//通过fromJS方法传入一个对象,得到一个immutable对象
const defaultStore = fromJS({
focused: false,
});
export default (store = defaultStore, action) => {
if (action.type == CHANGE_FOUCS_STATUS) {
// immutable对象的set方法,会结合之前immutable对象(也就是defaultStore)的值和要设置的值,返回一个全新的immutable对象,并不会对之前的对象做修改。
return store.set("focused",action.value);
}
return store;
};
- 由于store变成了一个immutable对象,所以获取值的方式也需要改变。
//header组件代码改动
const mapStateToProps = (state)=>{
return {
//由于
focused:state.header.get("focused")
}
}
6.在总的reducer.js中结合dedux-immutable库把最外层的reducer对象也变成immutable对象,改造代码如下:
// import { combineReducers } from "redux";
//引入包的时候combineReducers从redux-immutable引入就可以了。
import { combineReducers } from "redux-immutable";
import { reducer as HeaderReducer } from "../common/header/store";
const reducer = combineReducers({
header:HeaderReducer
});
export default reducer;
- 由于整个reducer返回的都是一个immutable对象,所以在页面中使用的时候都需要用get方法获取数据,改造header组件代码如下:
const mapStateToProps = (state)=>{
return {
// focused:state.get("header").get("focused")
//下面的写法等价于上面的写法
focused:state.getIn(["header","focused"])
}
}
7.搜索框聚焦,请求搜索历史数据,使用redux-thunk中间件,把异步请求数据操作写在actionCreator中。效果如下:
![](https://img.haomeiwen.com/i10007410/20a06dc189dccb8f.png)
- store\index.js文件 加入中间件redux-thunk
import { createStore, compose, applyMiddleware } from "redux";
import reducer from "./reducer";
import thunk from "redux-thunk";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
applyMiddleware(thunk)
));
export default store;
- actionCreators.js 导出函数,在该函数中请求异步数据并通过dispatch方法传递action给store
//注意一个小细节,由于header中的store数据是调用fromJS得到的immutable数据,所以此处把拿到的数据也转换成immutable数据
import { fromJS } from "immutable";
const setKeywordList = (list) => ({
type: KEYWORD_LIST,
list:fromJS(list)
});
//暴露出去一个函数
export const getKeywordList = (data)=>{
return async (dispatch)=>{
const {data} = await axios.get("http://127.0.0.1:5500/data/headerList.json");
const action = setKeywordList(data.list)
dispatch(action);
}
}
- header\store\reduce.js 代码改造,改变if为switch
import { CHANGE_FOUCS_STATUS,KEYWORD_LIST } from "./constant";
//引入immutable库,mutable"可变的"的意思,immutable"不可变的"意思
import { fromJS } from "immutable";
//通过fromJS方法传入一个对象,得到一个immutable对象
const defaultStore = fromJS({
focused: false,
list:["罗小黑","刺客伍六七"]
});
export default (store = defaultStore, action) => {
switch(action.type){
case CHANGE_FOUCS_STATUS:
// immutable对象的set方法,会结合之前immutable对象的值和要设置的值,返回一个全新的对象。
return store.set("focused",action.value);
case KEYWORD_LIST:
return store.set("list",action.list);
default:
return store
}
};
- header\index.js 头部组件
import { Component } from "react";
import { connect } from "react-redux";
import { actionCreators } from "./store";
class header extends Component {
//渲染历史记录
getHisTory(state, list) {
if (state) {
return (
<History>
{list.map((item) => {
return (
<div className="item" key={item}>
<div className="item__left">
<i className="iconfont icon-clock"></i>
<span>{item}</span>
</div>
<i className="iconfont icon-chahao"></i>
</div>
);
})}
</History>
);
}
}
render() {
const { focused,changeFoucs,list } = this.props;
return (
<NavLeft>
<p className="p1">首页</p>
<p className="p2">下载APP</p>
<div className={focused == true ? "search focus" : "search"}>
<input
type="text"
placeholder="搜索"
onFocus={() => {
changeFoucs(true);
}}
onBlur={() => {
changeFoucs(false);
}}
/>
<span className="iconfont icon-fangdajing"></span>
{this.getHisTory(focused, list)}
</div>
</NavLeft>
);
}
}
const mapStateToProps = (state) => {
return {
focused: state.getIn(["header", "focused"]),
list: state.getIn(["header", "list"]),
};
};
const mapActionToProps = (dispatch) => {
return {
changeFoucs(value) {
const action = actionCreators.changeFocusStatus(value);
dispatch(action);
//如果聚焦,就去请求后端数据
if(value)dispatch(actionCreators.getKeywordList())
}
};
};
export default connect(mapStateToProps, mapActionToProps)(header);
网友评论