useContext usage + switching theme small example + optimization of object literal delivery

We generally use Props to transfer parameters between parent and child components in react, but when encountering multi-layer transfer or brother component transfer, each layer of components needs to declare parameters for hierarchical transfer, which increases the complexity of the code and is not conducive to the maintenance of code logic. At this time we can use context for parameter passing.

The role of Context is to provide a technology for global shared data for the component tree it contains.

Let's show how to use useContext with a theme color call

1. Define theme colors

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

2. Use createContext to initialize

const ThemeContext = createContext(themes.light)

3. Create three components to show parameter passing

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

4. Create a provider component in the top level (parent component)

Use the ThemeContext.Provider created by context to wrap the component, and pass the theme color as value

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

5. Use useContext to import ThemeContext in the grandson component, then take out the value and apply it to the component

The parameter object received by the useContext function iscontext 自身

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

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

}

Then we found that the theme application has already taken effect.
insert image description here
Next, we will change the theme with one click!

We already know that the theme color applied in the grandchildren component is passed from the parent component, so what we want to change is the value passed from the provider of the parent component. In the parent component, our value is passed directly, so we want to theme.lightchange It just needs useStateto be used!

Yes, you read that right, change the value of useContext with useState. . .

Use useState to change the value of useContext

Method 1: Add theme switching method and trigger element in parent component

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>
    )
}

Other components do not need to be moved. After the parent component changes state, it will automatically pass the theme parameters and render~

Method 2: Add a theme switching method in the parent component, pass the method to the grandchild component, and trigger it in the grandchild component

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>)

}

insert image description here
insert image description here
We can see that it can be switched normally~

The subcomponents under the Provider component will be re-rendered after the value of the context changes, which may cause some performance problems. We can avoid unnecessary re-rendering memoby

 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 is determined by new and old value detection, using the same algorithm as Object.is
If it is passed to Content 字面对象, it is possible that every time the value is detected, the child component will be re-rendered

In our example above, the passed themeStyle object will only trigger re-rendering every time it is changed using setThemeStyle. If we use a literal object such as the one below to pass

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>)

}

In ContextDemo, we define a color state, and then use { { color }} to pass, this color is actually a shorthand for { {color: color }}, which means that our 每次color 变化must be 赋值given to an object, and the key and value are both color. In this way, every time our theme color changes 多了一次赋值操作, so every time we render, this attribute will be one 全新的对象, and === comparison with the previous attribute value will get false, and what is provided to the context is also a new literal object , causing the grandson component ThemeContent to follow in the rendering of the parent component every time重新渲染

So the solution is that we first generate a style object to store all

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

Similarly, our style is also optimized like this

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

However, in normal small-scale project development, our inline styles are generally assigned directly. After all, inline styles do not change so frequently~

This example is just the simplest example, and the help can help everyone learn the use of useContext together. There is also the optimization of object literal props and context~ If there is something wrong, please correct me gently~

insert image description here

Guess you like

Origin blog.csdn.net/weixin_38318244/article/details/127321410