简介
1 .类似于redux中的功能,但是这个传值怎么操作
import React,{useState,useEffect,useReducer}from 'react'
// 怎么传值进去呢,全都挂载到action里面
function reducer(state,action){
switch (action.type){
case 'add':
return {count:state.count+action.value};
case 'sub':
return {count:state.count-action.value};
default:
throw new Error()
}
}
function App(){
const [state,dispatch]=useReducer(reducer,{count:0})
return (
<>
点击次数 {state.count}
<button onClick={()=>dispatch({type:"add",value:10})}>+</button>
<button onClick={()=>{dispatch({type:"sub",value:1})}}>-</button>
</>
)
}
export default App
useReducer基础
1 .任何使用useState的地方,都可以替换成useReducer
2 .单例替代useState的例子
const initState=0
// const reducer=(state,action)=>{
// switch (action){
// case 'add':return state+1
// case 'sub':return state-1
// case 'reset':return 0
// default:throw new Error("Unexcepted action")
// }
// }
// 针对单个值,而且对每一个情况都进行了分类
// 传入第二个参数
const reducer=(state,action)=>{
switch(action.type){
case 'add':return state+action.value
case 'sub':return state-action.value
case 'reset':return 0
default:throw new Error("Unexcepted action")
}
}
<button onClick={()=>dispatch('add')}>+1</button>
<button onClick={()=>dispatch('sub')}>-1</button>
<button onClick={()=>dispatch('reset')}>0</button>
<button onClick={()=>dispatch({type:"add",value:10})}>+10</button>
<button onClick={()=>dispatch({type:"sub",value:1})}>-1</button>
<button onClick={()=>dispatch({type:"reset",value:0})}>reset</button>
3 .state是一个对象
const initState={
count1:0,
count2:1
}
const reducer=(state,action)=>{
switch(action.type){
case "add1":
return {...state,count1:state.count1+action.value}
// 对象的解构,先放入原来的对象,拿后面新的处理后的key覆盖前面已经放入的数据.相同的key的值,会使用后面的value
case "add2":
return {...state,count2:state.count2+action.value}
default:
return state
}
}
const [state,dispatch]=useReducer(reducer,initState)
const [state2,dispatch2]=useReducer(reducer,initState)
// 多次使用,可以使用同一份数据复制多多个模板,且互相之间不会影响
return (
<>
count1:{state.count1}
<button onClick={()=>dispatch({type:"add1",value:10})}>+10</button>
count2:{state.count2}
<button onClick={()=>dispatch({type:"add2",value:1})}>+1</button>
count2-1:{state2.count1}
<button onClick={()=>dispatch2({type:"add1",value:1})}>+1</button>
count2-2:{state2.count2}
<button onClick={()=>dispatch2({type:"add2",value:10})}>+10</button>
</>
)
其他笔记
1 .hooks每次render都有自己的props和state,可以认为每次render的内容都会形成一个快照保存下来,函数被销毁了,但是变量会被react报留下来
2 .当状态变更发生render的时候,就形成了n个render状态,而每个render状态读拥有自己固定不变的props和state。
3 .
useState和useReducer的区别
1 .react 内部,useState就是用useReducer实现的,useState返回的函数内部封装了一个dispatch
使用场景区别,为什么要使用reducer
1 .更容易管理大量状态-一个完整流程涉及到的一些相关变量适合用这个写,注意一定是要有关联的
function LoginPage() {
const [name, setName] = useState(''); // 用户名
const [pwd, setPwd] = useState(''); // 密码
const [isLoading, setIsLoading] = useState(false); // 是否展示loading,发送请求中
const [error, setError] = useState(''); // 错误信息
const [isLoggedIn, setIsLoggedIn] = useState(false); // 是否登录
const login = (event) => {
event.preventDefault();
setError('');
setIsLoading(true);
login({ name, pwd })
.then(() => {
setIsLoggedIn(true);
setIsLoading(false);
})
.catch((error) => {
// 登录失败: 显示错误信息、清空输入框用户名、密码、清除loading标识
setError(error.message);
setName('');
setPwd('');
setIsLoading(false);
});
}
return (
// 返回页面JSX Element
)
}
//useState实现登陆界面.
2 .更容易被其他开发者理解:这一条不算吧.可以被替代,如果用immtable-helper,修改起来数据也是非常容易被理解的
3 .更容易被测试.这个好像无法被替代.
4 .使用reducer的场景
1 .你的state是一个数组或者对象
2 .你的state变化很复杂,经常一个操作关联到很多state
3 .你希望构建自动化测试用例来保证程序的稳定性
4 .你需要在深层子组件里面去修改一些状态
5 .代码
import React,{useState,useCallback,useMemo,useContext,useReducer} from 'react'
// count的reducer:把一个数据的变化给你搞的清清楚楚
function reducer1(state,action){
// 接收当前的state,触发动作,返回最新的state
switch(action.type){
case 'add':
return {count:state.count+action.value};
case 'sub':
return {count:state.count-action.value}
default:
throw new Error()
}
}
// 这样看起来,一个数据就要写一个,有点类似于前后端的协议操作,相当之规范.immutable那种操作也是写在这里的.我觉得既然已经开始拆分了,那就贯彻到底.这样不是非常的清爽,如果一个里面有很多,那不就是根据dispatch的名字看不出来了么.
// name的reducer
function reducer2(state,action){
switch(action.type){
case 'change':
console.log(action)
// 没有数据变化的时候也会执行该操作,其实这里已经明确了你发出dispatch的时候已经肯定要发生改变了
return {name:action.value};
default:
throw new Error()
}
}
// reducre是一个纯函数,没有任何副作用,ui,相同的输入一定返回相同的输出,非常容易做单元测试
// 还有一个就是感觉是非常透明的,useState感觉还是隔着一层东西
// 操作注意竖向,处理的state必须是immutable,这意味着永远都不要直接修改参数中的state对象,reducer函数返回的每次都是一个新的state object
// 但是如果仅仅是为了便于测试和理解的化,
// 复杂流程的相关数据管理
const initState={
name:'刘华强',
pwd:'123456',
isLoading:false,
error:'',
isLoggedIn:false,
}
function loginReducer(state,action){
switch(action.type){
case "login":
return {
...state,
isLoading:true,
error:''
}
case "success":
return{
...state,
isLoading:false,
isLoggedIn:true,
}
case 'error':
return {
...state,
error:action.payload.error,
name:'',
pwd:'',
isLoading:false
}
default:
return state
}
}
export default function(){
const [stateCount,dispatchCount]=useReducer(reducer1,{count:0})
// 传入:操作,默认值
// 返回:当前的值,触发动作的函数
const [stateName,dispatchName]=useReducer(reducer2,{name:'刘华强'})
const [stateLogin,dispatch]=useReducer(loginReducer,initState)
const {name,pwd,isLoading,error,isLoggedIn}=stateLogin
// 这样取值不会每次都重新渲染吧..................啊啊啊啊啊啊啊啊啊啊啊啊
function login(){
dispatch({type:'login'})
if(Math.random>0.5){
// 模拟成功
setTimeout(()=>{
dispatch({type:'success'})
})
// 改变状态的时候确实爽多了,而且跟这一个操作相关的变量我们都一起修改了.避免漏改,或者改错.所以一个操作需要关联到更多的变量的时候,可以用这种方式来组织代码.
// 分开了how和what,让我们的代码可以像用户的行为一样,更加清晰
// 感觉就像搭积木一样,这样写代码让我知道我下一次可以写的更好
}else{
// 模拟失败
dispatch({type:"error",payload:{error:"超时了"}})
}
}
return(
<div>
<button onClick={()=>dispatchCount({type:"add",value:1})}>{stateCount.count}</button>
<button onClick={()=>dispatchName({type:"change",value:"菲菲"})}>{stateName.name}</button>
测试useReducer
<hr />
<h3 onClick={login}>啦啦啦</h3>
名字{name}
密码{pwd}
当前状态{isLoading===true?"正在登陆中":"登陆完成"}
登陆{error===true?"失败":'成功'}
是否登陆{isLoggedIn==true?"已经登陆":"没有登陆"}
</div>
)
}
跨组件修改信息结合context
1 .
SVN diff
1 .http://www.ruanyifeng.com/blog/2012/08/how_to_read_diff.html