context之于react-redux
context在props、state、refs三大属性外另一属性。可以使用context属性在祖组件中进行数据共享。
在react中有个createContext()可以创建context对象,返回的组件中有两个组件Provider、Consumer。
Provider组件要包裹子组件,并且只有一个value属性,值为要共享的数据。
Consumer组件是支持函数组件和类组件使用的,Provider仅能在类组件中使用。
import React from 'react';
const MyContext = React.createContext();
//MyContext.Provider还可以这样获取
const {Provider} = MyContext;
export default class App extends React.Component{
state = {thing:'apple'};
render(){
const {thing} = this.state;
return (
<div>
<div>这是父组件 App</div>
<div>父组件中有个{thing}</div>
//Provider要包裹子组件,让父组件的后代组件可以使用context对象
/**联想到react-redux中的Provider组件了
在入口文件中将App组件包裹起来让App下的其他组件都可以使用store
*/
//<MyContext.Provider value={thing}>
<Provider value={thing}>
<B/>
</Provider>
//<MyContext.Provider>
</div>
);
}
class B extends React.Component{
render(){
return (
<div>
<div>这是子组件 B</div>
<C/>
</div>
);
}
class C extends React.Component{
//在孙组件中使用context就要声明
static contextType = MyContext;
render(){
return (
<div>
<div>这是孙组件 C</div>
<div>孙组件中知道父组件有个{this.context}</div>
</div>
);
}
//如果想共享多个数据,value还可以是一个对象,那就是以下写法:
//<Provider value={{data:{a:'a'},thing:'apple'}}
//孙组件内获取的context就是this.context.thing
//要是孙组件是函数没法使用this怎么办呢?不怕,咱们还有Consumer组件
/**
function C(){
return (
<div>
<div>这是孙组件</div>
<div>孙组件中知道父组件有个
<MyContext.Consumer>
{ value =>value.thing }
</MyContex.Consumer>
</div>
};
}
*/
Fragment ——层级嵌套终结者
看以上代码,App组件是多层结构所以要有个父div盒子包裹,B组件、C组件也要用div包裹,那就造成了很多不必要的<div>
要想不使用div包裹每个组件,可以使用react中的Fragment组件包裹。它不会出现在HTML结构中。
当然,还有一个方法就是用<></>包裹。
import React,{Fragment} from 'react';
export default class App extends Component{
render(){
return (
<Fragment>
<div>这里有些东西</div>
<div>  :</div>
</Fragment>
);
}
PrueComponent与Component
较于Component,PrueComponent为类组件提供了更严格的重新渲染规则,提高了效率。
Component存在问题:
- 父组件重新渲染后不管子组件有没有使用父组件的状态,子组件也会被渲染。
- 调用this.setState({})没有改变状态也会重新渲染组件。
PrueComponent原理使用shouldComponentUpdata()生命周期,将前状态和后状态进行比较,相同返回false不重新渲染。
PrueComponent存在问题:
当你通过浅拷贝获取state对象后,进行状态的修改再传递给setState时,因为PrueComponent会对比引用地址,所以会造成无法重新渲染的情况。
import React,{Compoennt,Fragment} from 'react';
export default class App extends Component{
state = {apple:'apple'};
change = ()=>{
this.setState({});
}
//shouldComponentUpdata有两个参数,可以获取之后修改后的props和state
shouldComponentUpdata(nextProps,nextState){
//如果apple没改变,就不进行重新渲染
if(this.state.apple === nextState.apple){
return false;
}else{
return true;
}
}
render(){
console.log('render---A');
return (
<Fragment>
<div>这是A组件</div>
<B/>
<button onClick={this.change}>点击测试</button>
</Fragment>
);
}
class B extends React.Component{
//在子组件中也可以使用shouldComponentUpdata判断props是否改变
//shouldComponentUpdata(nextPorps){...}
render(){
console.log('render--B');
return (
<>
<div>这是子组件 B</div>
<C/>
</>
);
}
//只有一个state属性还好,但是如果有二十个state属性呢?
//就可以使用react提供的PrueComponent
//只用将App、B组件继承Component修改为继承PrueComponent就行
/**
PrueComponent存在问题:
state={apple:'apple',data:['test']};
change=()=>{
const {data} = this.state;
data.unshift('banana');
this.setState({...this.state,data});
//这时即使data数组确实改变了,但是引用地址是相同的,会被判定为未修改状态而不重新渲染
//所以修改状态最好就是传入新数据
this.setState({...this.state,data:[...this.state.data,'banana']});
//上面写的可能有点绕,可以看以下的例子理解一下
const obj = {a:'apple',b:'banana',c:'pear',b:'pineapple'};
console.log(obj.b);//pineapple
*/