在类组件下可通过 React.createRef() 或refs直接获取元素。
使用PropTypes对类型和必要性进行限制,defaultProps设置默认值。
unmountComponentAtNode只对ReactDOM.render挂载在顶层的组件才生效,其余无关render方法永远返回的都是false。
初始化
父组件的constructor-------父组件的componentWillMount-------父组件的render------子组件的constructor--------子组件的componentWillMount-------子组件的render----子组件componentDidMount--------父组件componentDidMount(先初始化渲染父组件然后渲染挂载子组件,最后挂载父组件,当组件销毁时先销毁父组件再销毁子组件)
setState更新
setState-------父组件的shouldCompnentUpdate------父组件的componentWillUpdate-------子组件的componentWillUpdate—父组件的componentDidUpdate(父组件更新之后也会先更新子组件,待子组件更新完毕后再触发父组件的更新完毕钩子)
forceUpdate更新
forceUpdate绕过shouldCompnentUpdate走更新流程
vue和react更新的不同,vue的父组件更新时只有当页面上有相关数据的更新才会去触发update函数,子组件也是一样,而react则是只要更新了数据,不管数据是否存在页面上,都会触发update更新函数。
href的链接如果是./xxxx或xxx的形式则是替换当前路径,如果是/xxx则替换所有路径
路由传参
风格一
声明式
传递
<Link to="/Home/Home/1">Home1</Link>
匹配
<Route path="/Home/Home1/:id" component={Home1}></Route>
接收
props.match.params.id
风格二
声明式
传递
<Link to="/Home/Home1?id=1">Home1</Link>
无须匹配
<Route path="/Home/Home1" component={Home1}></Route>
接收
通过qs库解析
function Home1(props) {
return <div>{qs.parse(props.location.search.slice(1)).id}</div>;
}
风格三 传state
声明式
<Link to={{ pathname: "/Home/Home2", state: { id: 2 } }}>Home22</Link>
无须匹配
<Route path="/Home/Home2" component={Home2}></Route>
通过state接收
function Home2(props) {
console.log(props.location.state);
return <div>{props.location.state.id}</div>;
}
如果是编程式导航则直接调用props里的history里的push与replace方法。
只有state种传参在打开新页面会为undifine所以要注意空处理,其他两种形式,只要路由保证格式一致,组件就会自动封装数据。
可以使用withRouter让一般组件可以使用history
在线切换主题
1.使用css变量
// 定义变量
:root {
--main-bg-color: red;
}
// 使用变量
.box {
width: 100px;
height: 100px;
background-color: var(--main-bg-color);
}
// 修改变量
document.getElementsByTagName("html")[0].style.setProperty("--main-bg-color", "yellow");
Redux
redux包含store,action,reducer
// 同步action
export var createIncrement = (data) => ({ type: Increment, data });
// 异步action,如果不想在组件内做异步操作,可以开启异步action
export var createAsynIncrement = (data, time) => {
return () => {
setTimeout(() => {
store.dispatch(createIncrement(data));
}, time);
};
};
// 开启异步action需要引入
import thunk from "redux-thunk";
const store = createStore(countReducer, applyMiddleware(thunk));
componentDidMoun和useEffect被执行两次可以去除<React.StrictMode>
setState在事件函数中是异步的,并且最终会合并,并且取最后一次执行的值,而setState在异步环境中则为同步执行(为什么?如何识别同步异步环境?)。
在函数式组件中的异步环境useState会有问题,如果不使用函数式更新则一直为原值,因为在定时器里根本就没有改原来的state值,而是改了useState里的值,第二次取出来的才是新值。
export default function Funtest() {
console.log("渲染開始");
const [state, setstate] = React.useState(0);
console.log("state", state);
React.useEffect(() => {
console.log("useEffect被執行了");
setInterval(() => {
console.log("state一直为0");
setstate(state + 1);
// setstate((state) => {
// console.log("定時器開始執行", state);
// return state + 1;
// });
}, 2000);
}, []);
return (
<div>
{state}
<button
onClick={() => {
setstate(state + 1);
}}
>
加一
</button>
</div>
);
}
祖孙传值时也可使用Context.provider,记住得用static contextType声明
Purecomponent只是简单的地址对比,如果没有改地址也就不会更新。
当子组件发生错误时,父组件可以使用getDerivedStateFromError,而vue可以使用errorCaptured处理
可以使用Navigate组件实现声明式主动跳转。
react-router6不支持类组件获取路由参数,函数组件可以使用useParams,useSearchParams,useLocation分别获取params,search和state。编程式路由导航则使用useNavigate
babel会把函数,类,jsx语法转译成React.createElement,只不过类型不一样。ReactDOM.render会根据类型去渲染。
React原理
1.通过babel把函数标签,类标签,jsx转化成React.createElement(type, config, children),这里会生成虚拟dom对象,把config全部放置在props上,如果有ref或者key属性,会在props上设置这两个属性的get方法。
普通标签会生成:
{
$$typeof: Symbol(react.element),
"type": "span",
"key": "1",
"ref": "mySpan",
"props": {
"name": "wth",
"self": "mySelf",
"source": "mySource",
"children": "我是span",
onClick: fn
},
"_owner": null,
"_store": {}
}
函数标签会生成:
{
$$typeof: Symbol(react.element),
"key": null,
"ref": null,
"props": {
"children": [
{
"type": "span",
"key": "1",
"ref": "mySpan",
"props": {
"name": "wth",
"self": "mySelf",
"source": "mySource",
"children": "我是span"
},
"_owner": null,
"_store": {}
},
"2"
]
},
"_owner": null,
"_store": {}
}
2.通过ReactDOM.render(element, container, callback) 开始进入render,大致分为3个阶段。
初始化阶段
legacyRenderSubtreeIntoContainer(null, element, container, false, callback)