useContext用法+切换主题小示例+对象型字面量传递的优化

react中父子组件传递参数我们一般使用Props,但是碰到多层传递或者兄弟组件传递的时候,每层的组件都需要声明参数进行层级传递,增加代码复杂程度的同时也不利于代码逻辑的维护。这时候我们就可以使用context进行参数传递了。

Context的作用就是对它所包含的组件树提供全局共享数据的一种技术。

我们以一个主题色的调用来展现一下useContext的使用方法

1.定义主题颜色

const themes = {
    
    
    light: {
    
    
        foreground: '#000',
        background: '#eee'
    },
    dark: {
    
    
        foreground: '#fff',
        background: '#666'
    }
}

2.使用createContext来初始化

const ThemeContext = createContext(themes.light)

3.创建三个组件来展示参数传递

// 主题应用组件
const ThemeContent= () => {
    
    
	return <div>This is ThemeContent</div>
}
// 这层暂时仅仅用于传递
const Toolbar = () => {
    
    
	return <ThemeContent></ThemeContent>
}
// 这层为父组件
export const ContextDemo = () => {
    
    
	return <Toolbar></Toolbar>
}

4.在顶层(父组件)中创建provider组件

使用context创建的ThemeContext.Provider来包裹组件,将主题色作为value值传递

export const ContextDemo = () => {
    
    
 <ThemeContext.Provider value={
    
    theme.light}>
	<Toolbar></Toolbar>
 </ThemeContext.Provider>
}

5.在孙子组件中使用useContext导入ThemeContext,然后取出value值,应用于组件之中

useContext 函数接收的参数对象是 context 自身

const ThemeContent = () => {
    
    
    const themeStyle = useContext(ThemeContext)

    return (<div>
        <div style={
    
    {
    
     background: themeStyle.background, color: themeStyle.foreground }}>
            hello world
        </div>
    </div>)

}

然后我们发现,主题应用已经生效了哈
在这里插入图片描述
接下来我们实现一键更换主题!

我们已经知道,在孙子组件中应用的主题颜色是父组件传递过来的,那么我们要更改的就是父组件provider处传递的value值 ,在父组件中我们的value是直接传递的theme.light,那么我们想改变它,只需要用useState就可以了!

是的,你没看错,改变useContext的值用useState。。。

使用useState改变useContext的值

方法1:在父组件添加主题切换方法和触发元素

export const ContextDemo = () => {
    
    
	/*  1.使用useState创建一个新变量,赋初主题值*/
    const [themeStyle, setThemeStyle] = useState(themes.dark)  
    console.log('themes.dark', themes.dark)
    
	/* 2.添加一个切换方法*/
    const toggleTheme = () => {
    
     
        setThemeStyle(style => style === themes.dark ? themes.light : themes.dark)
    }

    return (
        <ThemeContext.Provider value={
    
     themeStyle }> /*  传递多参数要用{
    
    {}}括起来哦*/
            <Toolbar></Toolbar>
            /*  3.添加一个按钮绑定此方法*/
            <button onClick={
    
    () => toggleTheme()}>change theme</button> 
        </ThemeContext.Provider>
    )
}

其他组件不需要动哦,父组件改变状态后会自动传递主题参数并渲染的~

方法2:在父组件添加主题切换方法,将方法传递到孙子组件,在孙子组件触发

export const ContextDemo = () => {
    
    
	/*  1.使用useState创建一个新变量,赋初主题值*/
    const [themeStyle, setThemeStyle] = useState(themes.dark)  
    console.log('themes.dark', themes.dark)
    
	/* 2.添加一个切换方法*/
    const toggleTheme = () => {
    
     
        setThemeStyle(style => style === themes.dark ? themes.light : themes.dark)
    }

    return (
        <ThemeContext.Provider value={
    
    {
    
     themeStyle, toggleTheme }}> /*  传递多参数要用{
    
    {}}括起来哦*/
            <Toolbar></Toolbar>
        </ThemeContext.Provider>
    )
}

/* 4.取出参数传递的切换方法并绑定在元素上*/
const ThemeContent = () => {
    
    
    const {
    
     themeStyle, toggleTheme } = useContext(ThemeContext)
    console.log(66666666666, themeStyle, toggleTheme)

    return (<div>
        <div style={
    
    {
    
     background: themeStyle.background, color: themeStyle.foreground }}>
            hello world
        </div>

        <button onClick={
    
    () => toggleTheme()}>change theme</button> 
    </div>)

}

在这里插入图片描述
在这里插入图片描述
我们看到是可以正常切换的哈~

存在于Provider 组件下的子组件在context 的值发生变化后,都会重新渲染,这样可能会引发一些性能问题,我们可以通过 memo 去避免一些不必要的重渲染

 const ThemeContent = memo(() => {
    
    
    const {
    
     themeStyle, toggleTheme } = useContext(ThemeContext)
    console.log(66666666666, themeStyle, toggleTheme)

    return (<div>
        <div style={
    
    {
    
     background: themeStyle.background, color: themeStyle.foreground }}>
            hello world
        </div>

        <button onClick={
    
    () => toggleTheme()}>change theme</button> 
    </div>)

})

Content是通过新旧值检测来确定变化,使用了与 Object.is 相同的算法
如果传给Content是个字面对象的话,有可能每次值的检测都会值得子组件重新渲染

上面我们的例子中,就是传递的themeStyle对象,只有每次使用setThemeStyle改变的时候才会触发重新渲染,如果我们使用诸如下边的字面量对象传递

export const ContextDemo = () => {
    
    
	/*  1.使用useState创建一个新变量,赋初主题值*/
    const [themeColor, setThemeColor] = useState('dark')  
    
	/* 2.添加一个切换方法*/
    const toggleTheme = () => {
    
     
        setThemeColor(color=> color=== 'dark' ? 'light' : 'dark')
    }

    return (
        <ThemeContext.Provider value={
    
    {
    
     themeColor }}> /*  传递多参数要用{
    
    {}}括起来哦*/
            <Toolbar></Toolbar>
            /*  3.添加一个按钮绑定此方法*/
            <button onClick={
    
    () => toggleTheme()}>change theme</button>
        </ThemeContext.Provider>
    )
}

const ThemeContent = () => {
    
    
    const {
    
     themeColor } = useContext(ThemeContext)
    console.log(66666666666, themeStyle, toggleTheme)

    return (<div>
        <div style={
    
    {
    
     background: themes[themeColor].background, color: themes[themeColor].foreground }}>
            hello world
        </div>
    </div>)

}

在ContextDemo中,我们定义一个color state,然后使用{ { color }}传递,这个color其实是{ {color: color}}的简写,意味着我们每次color变化都要赋值给一个对象,key和value都是color,这样每次我们主题颜色变化的时候都多了一次赋值操作,所以每次渲染时,这个属性都会是一个全新的对象,与之前的属性值进行===比较将得到false,提供给context的也都是一个新的字面量对象,导致孙子组件ThemeContent每次都会在父组件的渲染中跟着去重新渲染

所以解决办法就是我们先生成一个用来存储所有的style对象

 const [themeStyle, setThemeStyle] = useState(themes.dark)  

同样的,我们的style也优化成这样

<div style={
    
    {
    
     background: themes[themeColor].background, color: themes[themeColor].foreground }}>
     hello world
</div>

不过在正常小型项目开发中,我们的行内样式style 一般也都是直接去赋值的,毕竟行内样式的改变不是那么的频繁~

这个例子只是一个最简单的例子,帮助能帮助到大家一起学习useContext的使用。还有对象字面量型props、context的优化~ 如果有不对的,还请大家温柔指正哦~

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_38318244/article/details/127321410