30
loading...
This website collects cookies to deliver better user experience
useState
Hook for local state management. If you’ve worked with it before, then you might have wondered why global state management can’t be just as easy. Why do we still need so much boilerplate to manage state with the Context API? What if we don’t want to be constrained by opinionated tools like Redux Toolkit or forced to use actions and reducers in our React applications?useState
Hook. In fact, with Hookstate, creating a global state is just as easy as creating a local state with the useState
Hook. In addition to its simplicity, Hookstate also extends our created state instance with other useful features.@hookstate/core
package, but Hookstate has several optional plugins that enable us to extend or customize our state Hooks — and the library’s documentation is well-written and packed with good demos. Below are some noteworthy plugins:@hookstate/persistence
enables us to persist our state to the browser’s local storage, which is useful for offline apps or if you’d like a user to retain their state data after reloading the page@hookstate/validation
can be very useful for form fields because it enables validation and error/warning messages for a state@hookstate/broadcasted
is a very useful tool if you want to enable synchronization of your state across different browser tabscreate-react-app
package to generate a new React application. We’ll run the following command in our terminal:npx create-react-app hookstate-chat
cd
into our new hookstate-chat
directory and install Hookstate:cd hookstate-chat
npm install --save @hookstate/core
react-custom-chat
. I created this npm package for this article so that we can focus on Hookstate and not have to bother ourselves with the design of our chat app, but you can use or build another, if you like:npm install --save react-custom-chat
react-custom-chat
installed, let’s go over to the ./src
directory and create a JSX file for our first chat component. We’ll name it FirstPerson
../src/FirstPerson.jsx
file:import ChatBox from 'react-custom-chat'
const FirstPerson = () => {
return (
<ChatBox
messageList={[]}
onSendMessage={newMessage => {}} // do something with newMessage
settings={{
position: 'left',
navColor: 'green',
navText: 'Mycroft'
}}
/>
)
}
export default FirstPerson
FirstPerson
component, we started by importing ChatBox
from the react-custom-chat
package. The ChatBox
component has a messageList
prop, which will contain an array of our message objects. Each message object will contain:text
property of type string
, which represents the message textperson
property of type string
, which can either be “primary” or “secondary”person
property helps the ChatBox
component to determine how to style each message.onSendMessage
prop expects a function that’ll tell it what to do whenever a message is sent.settings
prop to define the look of our chat boxes. In this case, we want the FirstPerson chat box to be positioned on the left of our page.SecondPerson
in our ./src
directory and paste the following code in it:import ChatBox from 'react-custom-chat'
const SecondPerson = () => {
return (
<ChatBox
messageList={[]}
onSendMessage={() => {}}
settings={{
position: 'right',
navColor: 'blue',
navText: 'Cortana'
}}
/>
)
}
export default SecondPerson
ChatBox
component in the react-custom-chat
documentation../src/App.js
file. Let’s replace what’s currently there with the following code:import FirstPerson from './FirstPerson'
import SecondPerson from './SecondPerson'
const App = () => {
return (
<>
<FirstPerson />
<SecondPerson />
</>
);
}
export default App
npm start
on our terminal. We should see a page that looks like this when we open our application in the browser:store.js
in the ./src
directory, which will house our application’s global state../src/store.js
file, we’ll use the createState
method from Hookstate to create our state:import { createState } from '@hookstate/core'
const store = createState({
firstPersonMessageList: [],
secondPersonMessageList: []
})
export default store
FirstPerson
and SecondPerson
states individually:...
const firstPersonMessageList = createState([])
const secondPersonMessageList = createState([])
...
createState
method with an initial object state that contains the properties firstPersonMessageList
and secondPersonMessageList
.useState
Hook from Hookstate. Since it’s a React Hook, we’ll need to call it inside our React components. Our returned state from the useState
Hook will have:get()
method we can use to get the state dataset()
method for setting a new value for our statemerge()
method for adding data to our state./src/FirstPerson.jsx
file and import the useState
Hook from Hookstate. We’ll also import our store from the store.js
file:import { useState } from '@hookstate/core'
import store from './store'
...
useState
Hook to access our store. Since useState
is a React Hook, we’ll need to use it inside the body of our FirstPerson
component. Let’s create a variable named globalState
and call the useState
Hook with our imported store as its value....
const FirstPerson = () => {
const globalState = useState(store)
...
}
export default FirstPerson
globalState
variable should contain the initial state we provided to our store. We can also directly destructure the firstPersonMessageList
and secondPersonMessageList
properties when using the useState
Hook to access our store. Let’s change our globalState
variable declaration to the following line of code:const { firstPersonMessageList, secondPersonMessageList } = useState(store)
firstPersonMessageList
and secondPersonMessageList
as individual states.globalState
variable would have had. We can now equally use the get()
, set()
, and merge()
methods in our destructured properties.sendMessage
event. We’ll name it handleSendMessage
:...
const handleSendMessage = newMessage => {
firstPersonMessageList.merge([{text: newMessage, person: 'primary'}])
secondPersonMessageList.merge([{text: newMessage, person: 'secondary'}])
}
handleSendMessage
and have provided it with a parameter named newMessage
. Our newMessage
parameter represents whatever our user types into the chat input field. For every new primary message we add to the firstPersonMessageList
, we’re also making a corresponding secondary addition to the secondPersonMessageList
. We’ll do the reverse when we get to the SecondPerson
component.merge()
method. If we were using the set()
method or React’s built-in useState
Hook, our function would look similar to this:const handleSendMessage = newMessage => {
firstPersonMessageList.set([...firstPersonMessageList, {text: newMessage, person: 'primary'}])
secondPersonMessageList.merge([...secondPersonMessageList, {text: newMessage, person: 'secondary'}])
}
merge()
method, if the current state value and the argument are both arrays, Hookstate will do the job of concatenating the current value with the value of the argument and setting it to the state. You can see other ways to use the merge()
method in the Hookstate documentation.secondPersonMessageList
state update by 500 milliseconds:...
const handleSendMessage = newMessage => {
firstPersonMessageList.merge([{text: newMessage, person: 'primary'}])
setTimeout(() => {
secondPersonMessageList.merge([{text: newMessage, person: 'secondary'}])
}, 500)
}
...
handleSendMessage
function as our ChatBox
onSendMessage
prop value. We’ll also use the get()
method from our firstPersonMessageList
to access our state, and then use it as the value for our ChatBox
messageList
prop:...
<ChatBox
messageList={firstPersonMessageList.get()}
onSendMessage={handleSendMessage}
settings={{
position: 'left',
navColor: 'green',
navText: 'Mycroft',
isOpen: true
}}
/>
...
FirstPerson.jsx
file should now look like this:import { useState } from '@hookstate/core'
import ChatBox from 'react-custom-chat'
import store from './store'
const FirstPerson = () => {
const { firstPersonMessageList, secondPersonMessageList } = useState(store)
const handleSendMessage = newMessage => {
firstPersonMessageList.merge([{text: newMessage, person: 'primary'}])
setTimeout(() => {
secondPersonMessageList.merge([{text: newMessage, person: 'secondary'}])
}, 500)
}
return (
<ChatBox
messageList={firstPersonMessageList.get()}
onSendMessage={handleSendMessage}
settings={{
position: 'left',
navColor: 'green',
navText: 'Mycroft'
}}
/>
)
}
export default FirstPerson
SecondPerson.jsx
file. Since we’ve already explained the steps in detail, we can go ahead and paste the following code in our file:import { useState } from '@hookstate/core'
import ChatBox from 'react-custom-chat'
import store from './store'
const SecondPerson = () => {
const { firstPersonMessageList, secondPersonMessageList } = useState(store)
const handleSendMessage = newMessage => {
secondPersonMessageList.merge([{text: newMessage, person: 'primary'}])
setTimeout(() => {
firstPersonMessageList.merge([{text: newMessage, person: 'secondary'}])
}, 500)
}
return (
<ChatBox
messageList={secondPersonMessageList.get()}
onSendMessage={handleSendMessage}
settings={{
position: 'right',
navColor: 'blue',
navText: 'Cortana'
}}
/>
)
}
export default SecondPerson
handleMessage
function for our SecondPerson
component, we’ve done the reverse of what we did in the FirstPerson
component: whenever a message is sent, it gets added as primary to the secondPersonMessageList
and as secondary to the firstPersonMessageList
.react-custom-chat
package and our demo app. If you want to keep in touch, consider subscribing to my YouTube channel and following me on GitHub. Keep building!