Basic concepts
To understand how to use the library you should only understand 3 basic things
State
State is a just a dataset. For example a simple state that contains user name could be represented as
Copy {
name : "Luke Skywalker"
}
Store
Store is a JavaScript class that have a state
property and setState
method.
Copy import { SharedStore } from 'react-shared-state'
class MyStore extends SharedStore {}
State
property is an empty object by default
Copy const store = new MyStore ()
console .log ( store .state) // {}
To update the state
you should use setState
method.
Copy store .setState ({name : "Luke SkyWalker" })
console .log ( store .state) // {name: "Luke SkyWalker"}
It works the same as react's setState method
Don't update state directly or your changes will be ignored
Right now setState accepts only object to update the state. Using updater as a function doesn't supported
Provider
Provider is a component that keeps your store and pass the store to your components.
To create provider call createProvider
Copy const NameProvider = createProvider ()
You add it to the root component of your application.
Copy import { NameProvider } from './simple-provider'
export function App () {
return (
// We fill default state with a name
< NameProvider initialState = {{ name : 'Luke SkyWalker' }}>
...
</ NameProvider >
)
}
After that you can use it in your components using YourProvider.connect
Copy import { NameProvider } from './name-provider.js'
const HelloComponent = (props) => {
< div >
< h1 > Hello, { props .name}</ h1 >
< button onClick = {() => props . store .setState ({ name : 'Darth Vader' })}>Set Name</ button >
</ div >
}
export const Hello = NameProvider .connect ((store , ownProps) => ({
store ,
name : store . state .name
}))(HelloComponent)
Connect is a High Ordered Component that accepts a mapStoreToProps
callback with two arguments:
store
— a store instance being automatically created
ownProps
— props passed to the component
To update the state you can use store.setState()
To access the state use store.state
Extending store
While you can use setState
to change you state it's better to hide your logic with custom methods implementation
For example instead of setting name using setState
it's better to add a custom method setName
.
First of all let's create a Store class and pass it to createProvider
.
Copy import { createProvider , SharedStore } from 'react-shared-state''
class NameStore extends SharedStore {
setName = (newName) => {
this .setState ({
name : newName
})
}
}
const NameProvider = createProvider ( 'name_provider' , NameStore)
Now you can use it in your component in props
Copy import { NameProvider } from './name-provider.js'
const HelloComponent = (props) => {
return < div >
< h1 > Hello, { props .name}</ h1 >
< button onClick = {() => props .setName ({ name : 'Darth Vader' })}>Set Name</ button >
</ div >
}
export const Hello = NameProvider .connect ((store , ownProps) => ({
setName : store .setName ,
name : store . state .name
}))(HelloComponent)
Since we've used class properties definition ()=>
to declare the method we can pass it in mapStoreToProps
.
Never pass setState
method in mapStoreToProps
because it's not declared as property in base state class and will fail to set state.
Using State with API calls
Sometimes you have to get some data from the external source, for example API. Wonder where to add it? Just add it the store!
Copy import { createProvider , SharedStore } from "react-shared-state" ;
class NameStore extends SharedStore {
setName = newName => {
this .setState ({ name : newName });
};
fetchName = () => {
this .setState ({ loading : true });
fetch ( "https://swapi.co/api/people/2/?format=json" )
.then (resp => resp .json ())
.then (data => {
this .setState ({ name : data .name });
this .setState ({ loading : false });
});
};
}
export const NameProvider = createProvider ( '' , NameStore);
And use as a simple call
Copy const HelloComponent = props => {
if ( props .loading) {
return < h1 >Loading...</ h1 >;
}
return (
< div >
< h1 > Hello, { props .name}</ h1 >
< button onClick = {() => props .setName ( "Darth Vader" )}>Set Name</ button >
< button onClick = {() => props .fetchName ()}>Fetch Name</ button >
</ div >
);
};
export const Hello = NameProvider .connect ((store , ownProps) => ({
fetchName : store .fetchName ,
setName : store .setName ,
name : store . state .name ,
loading : store . state .loading
}))(HelloComponent);
Also we've added a loading
flag to indicate something is going one.
Working example available at https://codesandbox.io/s/wy308n0k88
Logging
You can add logging for all stores
Copy import { ProviderComponent} from 'react-shared-state'
ProviderComponent . DEBUG = true