Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set zustand state in a class component

I am working on a site that has a piece a global state stored in a file using zustand. I need to be able to set that state in a class component. I am able to set the state in a functional component using hooks but I'm wondering if there is a way to use zustand with class components.

I've created a sandbox for this issue if that's helpful: https://codesandbox.io/s/crazy-darkness-0ttzd

here I'm setting state in a functional component:

function MyFunction() {
  const { setPink } = useStore();

  return (
    <div>
      <button onClick={setPink}>Set State Function</button>
    </div>
  );
}

my state is stored here:

export const useStore = create((set) => ({
  isPink: false,
  setPink: () => set((state) => ({ isPink: !state.isPink }))
}));

how can I set state here in a class componet?:

class MyClass extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <div>
        <button
          onClick={
            {
              /* setPink */
            }
          }
        >
          Set State Class
        </button>
      </div>
    );
  }
}
like image 955
whoMe Avatar asked Feb 07 '21 04:02

whoMe


People also ask

How do you set state in class component?

To change a value in the state object, use the this. setState() method. When a value in the state object changes, the component will re-render, meaning that the output will change according to the new value(s).

Can we use state in class component?

With React 16.8, function components can now use state. Before this, data from the state had to be passed down as props from class components to function components or you had to convert your function component to a class component. Now, we can use React hooks, and to use state we can use the useState hook.

How do you change the state of a component?

We have to set initial state value inside constructor function and set click event handler of the element upon which click, results in changing state. Then pass the function to the click handler and change the state of the component inside the function using setState.

What is the use of create function in Zustand?

The create function tells Zustand to create a store for us. create takes in a callback function as its first argument, which accepts a set function that is used for manipulating states. Line 4: Our useStore variable will have one state, called dollars. Its initial value will be 0. Line 5: Declare another state called broke.

How do I install Zustand in react?

To install Zustand in your React project, run the following terminal command: To get started, Zustand first needs a store. This is where you will put all your states and their functions. For now, we will only store state variables. Create a separate file called states.js and write the following code: Line 3: Create a custom hook called useStore.

How do I create a store in Zustand?

Create a separate file called states.js and write the following code: Line 3: Create a custom hook called useStore. The create function tells Zustand to create a store for us. create takes in a callback function as its first argument, which accepts a set function that is used for manipulating states.

What are some use cases for Zustand state variables?

Some use cases include: Validation: Throw an error if the new state value does not follow a specific format. Boundary values: Don’t let our state variable exceed a certain value. For example, our dollar variable should not be more than 10 or less than 0. For middleware, Zustand developers recommend using the immer module to set state values.


3 Answers

A class component's closest analog to a hook is the higher order component (HOC) pattern. Let's translate the hook useStore into the HOC withStore.

const withStore = BaseComponent => props => {
  const store = useStore();
  return <BaseComponent {...props} store={store} />;
};

We can access the store as a prop in any class component wrapped in withStore.

class BaseMyClass extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    const { setPink } = this.props.store;
    return (
      <div>
        <button onClick={setPink}>
          Set State Class
        </button>
      </div>
    );
  }
}

const MyClass = withStore(BaseMyClass);
like image 170
Jemi Salo Avatar answered Sep 17 '22 14:09

Jemi Salo


Seems that it uses hooks, so in class you can work with the instance:

import { useStore } from "./store";

class MyClass extends Component {
  render() {
    return (
      <div>
        <button
          onClick={() => {
            useStore.setState({ isPink: true });
          }}
        >
          Set State Class
        </button>
      </div>
    );
  }
}

Edit gracious-frost-9vdu3

like image 41
Dennis Vash Avatar answered Sep 19 '22 14:09

Dennis Vash


Create a React Context provider that both functional and class-based components can consume. Move the useStore hook/state to the context Provider.

store.js

import { createContext } from "react";
import create from "zustand";

export const ZustandContext = createContext({
  isPink: false,
  setPink: () => {}
});

export const useStore = create((set) => ({
  isPink: false,
  setPink: () => set((state) => ({ isPink: !state.isPink }))
}));

export const ZustandProvider = ({ children }) => {
  const { isPink, setPink } = useStore();

  return (
    <ZustandContext.Provider
      value={{
        isPink,
        setPink
      }}
    >
      {children}
    </ZustandContext.Provider>
  );
};

index.js

Wrap your application with the ZustandProvider component.

...
import { ZustandProvider } from "./store";
import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <StrictMode>
    <ZustandProvider>
      <App />
    </ZustandProvider>
  </StrictMode>,
  rootElement
);

Consume the ZustandContext context in both components

MyFunction.js

import React, { useContext } from "react";
import { ZustandContext } from './store';

function MyFunction() {
  const { setPink } = useContext(ZustandContext);

  return (
    <div>
      <button onClick={setPink}>Set State Function</button>
    </div>
  );
}

MyClass.js

import React, { Component } from "react";
import { ZustandContext } from './store';

class MyClass extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <div>
        <button
          onClick={this.context.setPink}
        >
          Set State Class
        </button>
      </div>
    );
  }
}

MyClass.contextType = ZustandContext;

Swap in the new ZustandContext in App instead of using the useStore hook directly.

import { useContext} from 'react';
import "./styles.css";
import MyClass from "./MyClass";
import MyFunction from "./MyFunction";
import { ZustandContext } from './store';

export default function App() {
  const { isPink } = useContext(ZustandContext);

  return (
    <div
      className="App"
      style={{
        backgroundColor: isPink ? "pink" : "teal"
      }}
    >
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <MyClass />
      <MyFunction />
    </div>
  );
}

Edit how-to-set-zustand-state-in-a-class-component

If you aren't able to set any specific context on the MyClass component you can use the ZustandContext.Consumer to provide the setPink callback as a prop.

<ZustandContext.Consumer>
  {({ setPink }) => <MyClass setPink={setPink} />}
</ZustandContext.Consumer>

MyClass

<button onClick={this.props.setPink}>Set State Class</button>
like image 29
Drew Reese Avatar answered Sep 18 '22 14:09

Drew Reese