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的优化~ 如果有不对的,还请大家温柔指正哦~