Coder Social home page Coder Social logo

risingstack / react-easy-state Goto Github PK

View Code? Open in Web Editor NEW
2.6K 42.0 104.0 17.89 MB

Simple React state management. Made with ❤️ and ES6 Proxies.

License: MIT License

JavaScript 100.00%
react javascript reactive-programming state-management es6-proxies state reactjs react-state

react-easy-state's People

Contributors

18thletter avatar allcontributors[bot] avatar brianrosamilia avatar buzinas avatar dan-gamble avatar dnlgrgly avatar f-adam-b avatar kubischj avatar moritzuehling avatar peteyycz avatar pontusab avatar rolandszoke avatar solkimicreb avatar stephanlough avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-easy-state's Issues

Component state not updated when using functions in store

Hi! Thanks for the great react easy state library.

I came across what I believe to be a bug when I decided to store functions in the store.

A reproducible testcase is available at https://github.com/jbq/react-easy-state-function-bug

To start it, type yarn && yarn start.

On the web page, click the Say Hi button, the message below still says Hello, World! instead of the expected Hi, World!.

Now in the source code of src/App.js change const hack_enabled = false to const hack_enabled = true and after app is reloaded click on the Say Hi button: the message is properly updated.

This is because when hack_enabled is turned off, only a function reference changes in the easy state store. And when it's turned on a string value is also changed to force the component update.

() => {

Hi,

I am starting to use it and I am amazed how much it saves comparing to using react state!
Love Proxy and what you are doing!

Small stuff:
Apparently declaring functions in the store with () => { does not work. The this no right.
Is it fixable ? I love these short expressions...

Thanks

Discourage direct mutation to global store.

In the contacts example the Contact component mutates the global store directly by doing

 Object.assign(this.props.contact, this.store.currentContact)

I assume it is for demonstration purpose for letting the users know that 'direct mutations' works too and that's great but according to the general practice direct mutation are bad for real life apps and there should be a function for it like you have for onDelete .

 store.deleteContact(this.props.contact)

I just want to suggest that wouldn't it be better to add a warning as comments in the code example or in the main readme saying that 'Though direct mutation to global store from a component works but it's not recommended as a general practice.'
Anyway nice library. 👍

Explain why everything must be wrapped

Multiple places in the documentation you say something like "Make sure to wrap all of your components with easyComp - including stateful and stateless ones - before you export them."

Why? If I have a stateless component with no added methods (say, a simple function-based component) am I required to wrap it, or is it just suggested?

Setting global store

I have global store like this:

const Store = easyStore({
  loading: false
});

Then I want to show loading component, I set Store.loading = true.
Indeed loading shows up.
Then after some procedure or after just delay I set Store.loading = false
And ... no reaction, no rerender.

I do the same inside setTimeout(() => ..., 1000)
And ... it rerenders correctly

I have no clue ... :)

Store JSX in Store.

Hey,

first of all, thanks for this great library. I started tinkering with it a bit and I have encountered an interesting error.

When I try to set jsx in an object like so:
let myObj = {test: <span>Test</span>};
then I can interpolate this object in my views just fine.

But when I wrap myObj in a store and try to call it, I get the following error:

TypeError: 'get' on proxy: property '_store' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '#<Object>' but got '[object Object]')

This error occurs in react in

  1132 |   }
  1133 | } else if (isValidElement(node)) {
  1134 |   // This element was passed in a valid location.
> 1135 |   if (node._store) {
  1136 |     node._store.validated = true;
  1137 |   }
  1138 | } else if (node) {

So I guess I can't store JSX in a Store. Is there any way around this?

Subcomponent rerender

Hello,

just found yesterday this amazing library (i've been following your projects nx-observe and nx-framework for some time).
So i wanted to check it out.

Now i have a small problem. I have to components: "parent" and "child".
The state of parent component looks like this: this.state = { user: { name: "John Doe" } };
I render the child component with user as a prop <Child user={this.state.user} />.

While changing the name in the parent or child component everything is fine this.state.user.name = "foo";. But when i change the complete user object it will propagate it to the child component but wouldn't rerender it this.state.user = { name: "bar" };. The parent component will be rerendered - the child component wouldn't.

Is it possible to do something like this?

React components not updating on store change

There is a good chance this is a just me issue. But I've been trying to figure out what's going on for a while with no success.

When I update my store, my components won't auto update to recognise the change.

Full repo: https://github.com/ro-savage/the-places-you-will-go
Code Sandbox: https://codesandbox.io/s/github/ro-savage/the-places-you-will-go
(react-easy-state 4.1.2, React 16.2, CRA 1.1.1)

Store

import { store } from 'react-easy-state'

const destinations = {
  add: (destination) => { 
    destinations.list.push(destination); 
    console.log(destination, destinations.list)
  },
  new: '',
  list: [
    'Canberra',
    'Auckland',
    'Berlin',
  ]
}

export default store(destinations)

Listing an array

import React from 'react'
import { view } from 'react-easy-state'

import destinations from '../destinationsStore.js'

const ListDestination = () => {
  return (
    <ul>
      {destinations.list.map(destination => {
        return (
          <li key={destination}>{destination}</li>
        )
      })}
    </ul>
  )
}

export default view(ListDestination)

Adding to array

import React from 'react'
import { view } from 'react-easy-state'

import destinations from '../destinationsStore.js'

const AddDestination = () => {
  const onClick = () => {
    destinations.add(destinations.new)
    destinations.new = ''
  }
  return (
    <div>
      <div>Choose a destination</div>
      <input 
          type="text" placeholder="Destination" 
          onChange={(e) => {destinations.new = e.target.value}} 
          value={destinations.new} 
       />
      <button onClick={onClick}>Add Destination</button>
    </div>
  )
}

export default view(AddDestination)

App

import React, { Component } from "react"
import "./App.css"

import { view } from 'react-easy-state'
import AddDestination from './destinations/AddDestination/AddDestination';
import ListDestinations from './destinations/ListDestinations/ListDestinations';


class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <AddDestination />
          <ListDestinations />
        </header>
      </div>
    )
  }
}

export default view(App)

TypeScript class support

Is there a way to get this to work with TS classes? It works if you use it as a function:

const View1 = ez.view(() => <div>{appState.count}</div>);

However I haven't been able to get it to work with class, e.g.:

class View2 extends React.Component<{count:number}, {}>{
	public render() {
		return <div>Hello from View2 {this.props.count}</div>;
	}
}

const View3 = ez.view(View2);

That gives me:

Uncaught TypeError: Class constructor View2 cannot be invoked without 'new'
    at new ReactiveHOC (es.es5.js:27)
    at constructClassInstance (react-dom.development.js:6801)
    at updateClassComponent (react-dom.development.js:8336)
    at beginWork (react-dom.development.js:8982)
    at performUnitOfWork (react-dom.development.js:11814)
    at workLoop (react-dom.development.js:11843)
    at HTMLUnknownElement.callCallback (react-dom.development.js:100)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:138)
    at invokeGuardedCallback (react-dom.development.js:187)
    at replayUnitOfWork (react-dom.development.js:11318)

I tried making a decorator but haven't figured that out yet unfortunately, all sorts of typing problems (I haven't done anything with decorators yet), but that seems like the ideal way to use it... In case it helps, my tsconfig.json is:

{
	"compilerOptions": {
		"experimentalDecorators": true,
		"jsx": "react",
		"moduleResolution": "node",
		"target": "es6"
	},
	"include": [
		"src/app/ts/*.ts",
		"src/app/tsx/*.tsx"
	]
}

💡 Features from Redux that I would like to see in react-easy-state

Here are a few things that I like from Redux that I think could enhance this great lib:

  • Single global store: it is very useful to have access to the whole store from any part of the application. Perhaps some documentation on how to do this properly would be useful.
  • Action log: in Redux I am able to console.log any action type in any reducer. This is very useful for debugging.

I made a simple counter app with this library and I really like it. I am considering rebuilding a work project to react using this state management library. Great work!

Modules and actions

Lovely little library, nicely done.

I have just a few questions about how I might integrate it into a decently-sized app. It would be good to see examples of how these things might be done:

  1. Vuex has the concept of modules, where you can keep functionality modular, but also communicate between modules (via root) to wire an app together. I'm wondering how this might be done in react-easy-state -an example of an app with multiple stores to explain this more clearly would be great.

  2. The actions seem a bit cumbersome, having to reference the store by name seems to mean that you can't define actions separately and then include them in a "config" that creates a store. So I'm wondering if a store basically needs to be a huge file with all actions inside. This kind of separation would be most welcome.

Cross-framework state management

The store code is framework independent, which could allow multiple frameworks to use the same stores for state management. Something like the below pseudo code:

import { store, reactView, infernoView } from 'easy-state'

const user = store({ name: 'userName' })

@reactView
export class ReactComp {
  render = () => <div>{user.name}</div>
}

@infernoView
export class InfernoComp {
  render = () => <div>{user.name}</div>
}

// the two components come from different frameworks but share the same piece of state

This would make multi-framework projects a lot easier to maintain. We could create a library - code named easy-state - which has a framework independent store implementation and different view implementations for a lot of different frameworks.

Currently I have a lot of projects in mind and won't have time to do this alone. If you have a favorite front-end framework and you are willing to create a view implementation for it, let me know. You can study the React view implementation, if you are interested.

Thanks! 🙂

Using this.setState doesn't trigger a re-render. Intentional?

Hi! Awesome library, trying to use it in my project now and realised that using the 'vanilla' component state doesn't seem to work. Is that intended, do I simply use a local store for my component for the same functionality?

if it's intended, perhaps it's worth including that component state shouldn't be used more explicitly in the docs (for people like me)?

Store functions vs separate ones

Hey, @solkimicreb

I've just discovered this library and decided to replace a few reducers and redux with it in my current project. Managed to do it with no difficulties and so far it works well. I've got a simple question, maybe opinionated:

const notificationsStore = store({
  notifications: [],
  addNotification (message) {
    notificationsStore.notifications.push(message)
  }
})

vs

const notificationsStore = store([])

function addNotification(message) {
  notificationsStore.push(message)
}

What do you think about this one, any pros/cons? What's your advice on this one?

The Future: React Easy Stack

Just a little heads-up on my future plans 🙂

This lib is part of my private React stack, which I plan to open-source eventually. Easy State and Easy Params are already open sourced and Easy Router will be the next.

With the release of the router this repo will turn into a monorepo. The npm packages will still be available separately (react-easy-state, react-easy-params and react-easy-router) and also as a complete React framework (react-easy-stack). They will continue to be developed separately, but the source code will get a new home - the react-easy-stack repo - with a unified documentation.

Seeing some odd issues after updating to 5.0.0 from 4.1.2

I know auto-bind was removed but did this change how stores behave? I wrote my own auto-bind once I saw that it was going to be removed but my stores appear to have some issues after the update. Read the release notes and it didn't mention any particular changes to stores but I see that the 'store.js' file is now gone and is now just exporting a plain observable. I think this may be a breaking change.

Persistence

How do you handle persistence of store objects? is there anything like redux persist? or do you have any suggestions about how to handle it?

view HoC is blocking react-router V4

I've been putting together an application and noticed this morning that when I have a component wrapped with view, it loses the ability to work with react router v4.

After some digging, it appears as though this bit of documentation explains what's happening.

I've been able to patch around this by wrapping my components using their withRouter HoC or wrapping the component in an empty <Route />, but apparently this is redundant and not optimal according to their documentation. Is there any way this project can propagate whatever react router (and likely others) needs to continue functioning?

Are there any plans for a "strict mode"?

Really liking this library, but I have one concern about using it in a larger codebase. Specifically I want to prevent mutations from happening outside store({}). I've been using Mobx (which has this functionality) but I'd happily switch over if something like this was included.

Example:

import { store, useStrict } from 'react-easy-state'

useStrict()

const counter = store({
  count: 0,
  increment() {
   this.count += 1
  }
})

counter.count++ // fails w/ warning

counter.increment() // works

create-react-app production build fails

Command: npm run build

Output:

> react-scripts build
3:44:35 PM: Creating an optimized production build...
3:44:43 PM: Failed to compile.
3:44:43 PM: Failed to minify the code from this file: 

./node_modules/@nx-js/observer-util/src/observer.js:8 

Read more here: http://bit.ly/2tRViJ9
3:44:43 PM:
3:44:43 PM: Build complete: exit code: 1
3:44:43 PM:
3:44:44 PM: Cleaning up docker container
3:44:44 PM: npm
3:44:44 PM:
3:44:44 PM: ERR!
3:44:44 PM: Linux 3.19.0-66-generic
3:44:44 PM: npm
3:44:44 PM:
3:44:44 PM: ERR!
3:44:44 PM: argv "/opt/buildhome/.nvm/versions/node/v6.11.2/bin/node" "/opt/buildhome/.nvm/versions/node/v6.11.2/bin/npm" "run" "build"
3:44:44 PM: npm
3:44:44 PM: ERR! node
3:44:44 PM: v6.11.2
npm
3:44:44 PM:
3:44:44 PM: Error running command: Build script returned non-zero exit code: 1
3:44:45 PM: ERR! npm v3.10.10
3:44:45 PM: npm
3:44:45 PM: ERR!
3:44:45 PM: An error occurred while building the site, skipping the deploy and cache refresh.
3:44:45 PM: code ELIFECYCLE
3:44:45 PM: npm ERR!
3:44:45 PM: [email protected] build: `react-scripts build`
npm
3:44:45 PM: ERR!
3:44:45 PM: Build script returned non-zero exit code: 1
3:44:45 PM: Exit status 1
npm
3:44:45 PM: ERR!
3:44:45 PM: npm
3:44:45 PM: ERR!
3:44:45 PM: Failed at the [email protected] build script 'react-scripts build'.
3:44:45 PM: npm ERR!
3:44:45 PM: Make sure you have the latest version of node.js and npm installed.
3:44:46 PM: npm ERR!
3:44:46 PM: If you do, this is most likely a problem with the rep-counter-client package,
npm
3:44:46 PM:
3:44:46 PM: ERR!
3:44:46 PM: not with npm itself.
npm
3:44:46 PM:
3:44:46 PM: ERR!
3:44:46 PM: Tell the author that this fails on your system:
npm ERR! react-scripts build
npm ERR!
3:44:46 PM: You can get information on how to open an issue for this project with:
3:44:46 PM: npm ERR!
3:44:46 PM: Finished processing build request in 49.305966188s
3:44:46 PM: npm bugs rep-counter-client
3:44:46 PM: npm
3:44:46 PM: ERR!
3:44:47 PM: Or if that isn't available, you can get their info via:
npm
3:44:47 PM: ERR!
3:44:47 PM: npm owner ls rep-counter-client
3:44:47 PM: npm
3:44:47 PM: ERR!
3:44:47 PM: There is likely additional logging output above.
3:44:47 PM:
3:44:47 PM: npm
3:44:47 PM: ERR! Please include the following file with any support request:
npm
3:44:47 PM: ERR! /opt/build/repo/npm-debug.log
3:44:47 PM: Cached NPM modules

Hook into proxy

I have a wish/suggestion to always do Store.x = 5 instead of Store.setX(5)
But upon this setting I want to do something before or after setting.
I know setters intended for this, but then I have to call it different name than the property....
Maybe it is not good writing, maybe I should do always setX(), everybody doing like this... Please advise

For example if I set todos[i].new = true
Then I trigger some function inside the store.

Could/Should I tap into your Proxy somehow ? Is it called middleware ?

No `jsnext:main` section?

A jsnext:main field that points to the ES6 version of the library's code. I see that package.json has the module field, but it points to the ES5 (transpiled?) code.

Static properties.

I was going through the source and found that the HOC will hide static properties on wrapped components. This is a known issue with HOC as mentioned here.

So, either we should add this thing in the documentation or figure out some robust solution.

Highlight platform support

Hi there!

Thanks for your great work on this library, looks really neat!

I have a concern, though. I feel like the "Platform Support" is burying the lede a bit... I imagine many devs will just assume that a React state library won't have any browser-support issues (because most JS is polyfill-able, the "common" browser-support concerns are things that touch the DOM, like styling).

I think it's a realistic scenario that someone might create a new app, spend 3 months building it out, and then right before release decide to test it on legacy browsers, only to discover that it's totally broken, and they have to perform a massive refactor to switch to a different state lib to get it to work.

I'm not saying you'd be to blame in this situation: you do make this point clear in the README. But, you know, not everyone reads documentation in its entirely. Especially newer developers who aren't familiar with ES proxies would be susceptible to this.

I think if we pulled the "Platform Support" section to the top, near "Installation", we reduce the risk of this happening dramatically. We could also bold the text about No IE Support

Also, this is a nit, but I think it's a bit disingenuous to say that "This may change in the future, if these platforms add ES6 Proxy support.", since Microsoft will never release another IE, so this will be perpetually broken in IE.

Happy to open a PR if you agree with me!

Hierarchical multi store

This is just my implementation for review.
Included the self workaround + observable in each sub-store. In order to access sub-store from inside.
Tried with classes, like in mobx, didn't work.

import { observable } from 'lib/observable'
import EndOfCall from './EndOfCall'

const Store = {
    EndOfCall,
    averageRating: 4,
}

export default observable(Store)
import navigate from './Navigate'
import { observable } from 'lib/observable'

const EndOfCall = {
    navigate,
}

const self = observable(EndOfCall)
export default self
import Store from 'Store'
import { observable } from 'lib/observable'

const navigate = {
    name: '',
    address: '',
    phone: '',

    get averageRating() {
        return Store.averageRating
    },

    setRating(e) {
        self.rating = e.target.value
    },

    get google() {
        return 'https://' + encodeURI(self.address)
    },

}

const self = observable(navigate)
export default self

Suggestion: Passing observable store as props

You may have already thought of this / decided it was a bad idea. My understanding of how all this works it pretty damn basic (never used observables or MobX/Reactive Framework)

What if you passed the observable as a prop to the component, so that you can still use propTypes and this behave a bit more as 'normal' react.

For example

import React from 'react'
import PropTypes from 'prop-types'
import { view } from 'react-easy-state'
import destinations from '../destinationsStore.js'

const ListDestination = (props) => {
  return (
    <ul>
      {props.destinations.list.map(destination => {
        return (
          <li key={destination}>{destination}</li>
        )
      })}
    </ul>
  )
}

ListDestination.propTypes = {
  destinations: PropTypes.shape({
    list: PropTypes.array,
  }),
}

export default view(ListDestination, {destinations})

Proxies polyfill

Hi,

As stated in the docs "This library is based on non polyfillable ES6 Proxies. Because of this, it will never support IE". Looking at something like proxy-polyfill, proxies seems to already have some polyfill to handle IE9+ support.

Is there a reason why this polyfill would not work with react-easy-state? (which part is non polyfillable, and is this for ever?)

ps. thanks for the great work: the simplicity of react-easy-state is a breeze!

`view` cannot copy static defaultProps getter

const defaultProps = { number: 0 };
let id = 0;

class SubComponent extends Component {
  static get defaultProps() {
    return { ...defaultProps, id: id++ };
  }

  // Other methods
}

export default view(SubComponent);

This throws an error for me: Uncaught TypeError: Cannot set property defaultProps of ...
It looks like view(BaseComponent) can't copy a static getter property.
Here is a Codesandbox link to an example of the error: https://codesandbox.io/s/x209v97ypq

MDN error link.

Is there a way to solve this?


Edit 1

Was just looking at view.js again. Could ReactiveHOC extend the passed in argument Comp instead of BaseComp? If so, would it still be necessary to copy the static properties to ReactiveHOC if it extended the Comp argument?

return class ReactiveHOC extends BaseComp {
    static displayName = Comp.displayName || Comp.name;
    static contextTypes = Comp.contextTypes;
    static childContextTypes = Comp.childContextTypes;
    static propTypes = Comp.propTypes;
    static defaultProps = Comp.defaultProps;
    ...
}

Could that just be

return class ReactiveHOC extends Comp {
    ...
}

?


Edit 2

Nvm my question in Edit 1. I realized that making a HOC extending a functional component does not necessarily make it extend React.Component.

Would it be possible to check the value isStatelessComponent before copying the static properties?
So if isStatelessComponent is true, copy the static properties, otherwise make ReactiveHOC extend from Comp without copying static properties..?

Batch store updates

Hi there,

Thanks so much for this awesome library, loving the drop and go nature of it.

One issue I am facing at the moment is that of making store updates and those updates are triggering a render on each and every property I update. I would like to be able to batch an update to the store, i.e. edit multiple individual properties before it triggers its re-render of the view.

I'm currently using it like this:

export const UserStateStore = store<IUserStateStore>({
  initialUserCheckRan: false,
  isLoggedIn: false,
  user: {
    authLevels: [EAuthLevel.ANONYMOUS],
    photoUrl: null,
    username: "",
    email: "",
    uid: "",
    authNodeIds: [],
  },
  loginUser(user) {
    UserStateStore.user = user;
    UserStateStore.isLoggedIn = true;
    UserStateStore.initialUserCheckRan = true;
  }
});

As you can see I have that loginUser() function there. When that is called I'd like to update the user object, and the logged in state and set that our initial check for a currently logged in user has been run.

But each of those three changes are triggering a re-render in my view which is set to listen for those properties.

Is there anything in react-easy-state which works similar to mobx actions? I recall the actions in MobX waiting until the action function has finished before updating views.

Type definitions

would be cool to have typings for typescript
at least like so:

declare module 'react-easy-state' {
  export function store(obj: any): any;

  export function view(Comp: any, ref: any): any;

  export namespace store {
    const prototype: {};
  }

  export namespace view {
    const prototype: {};
  }
}

fuzzy search with fuse.js not updating in view

Has any one used react-easy-state with fuse.js for a fuzzy search?

http://fusejs.io/

  1. I assign a store to do a cities fuzzy search
    const localStore = store({ cities: [], displayedCities:[], name:'thing'});

  2. have a fuzzy filter on keydown in the input. Logging the localStore shows the expected out put, by UI won't show changes in the array. Athough will update localStore.name..
    `
    fuzzyFilter(event){
    let value = event.target.value;
    var options = {
    keys: ['name']
    }
    var fuse = new Fuse(localStore.displayedCities , options)

    let result = fuse.search(value)
    console.log(result);
    localStore.displayedCities = result;

    localStore.name = value;
    console.log(localStore);

    }

`

any ideas?

4.1.2 broken with Create React App v2

I am getting this error with create-react-app v2 (react-scripts 2.0.0-next.47d2d941), react 16.2.0 and react-easy-state 4.1.2 when wrapping a Class / Stateful component with view.

image

You can see the issue by cloning this repo: https://github.com/ro-savage/react-easy-state-cra-bug then running npm start. It will crash, then look at the Stateful.js component, remove view and it works fine.

It appears to work fine with CRA v1.

If you think its an issue with create-react-app rather than react-easy-state please cross post to their repo.

make autobind optional

I'm really liking the compact state management, however I'm not sure I want my methods to be automagically bound to the class instance.

Maybe something like export default view(HelloComp, { autoBind: true }) could be used to explicitly enable this feature?

React Native support

Hi, I just test this library in React Native 0.53.0, and it's work like a charm. If anyone can test on lower version and give some feedback this will be very nice.

How to connect two stores

Hi,

I'm trying to connect two stores which live in separated files, but changing the value on one, is not reflected on the other.

You can see an example here:

https://codesandbox.io/s/z6055yr7kp

I'm simulating an ajax request with the setTimeout and, you can see in the codesandbox console, when changing the role from 'user' to 'admin' in the userStore.js, the isLoading function from the store.js is called but the role value is not intercepted.

I'm not sure if this is a bug or just me making a stupid mistake.

ps.
thanks for this project! Is amazing!

Middlewares support

Hey.
I really like react-easy-state, it is easy enough to work with, simple, clean and not messy like redux or mobx.
One thing i really liked about redux is the fact you can pass middlewares, for example I can pass an error-logger middleware which logs all new state changes and in case of an error in the users browser i take a snapshot of all the stores, the input and operations the user did and try to recreate it to fix the bugs.
Are there plans for adding middlewares in the future or should I just wrap all my stores and call one function that does what I need instead ?

const { store }  = 'react-easy-state'

const UserStore = store({
    name:'',
    lastName:''
})

const OtherStore = store({
    otherName:'',
    otherLastName:'',
    updateUserName:function(name){
        console.log('updating with name', name)
        OtherStore.name = name
    }
})

const loggerMiddleware = (...args) => {
    console.log(`this is logger middleware`, args)
}

const otherMiddleware = (...args) => {
    console.log(`this is other middleware`, args)
}

// if array, map on it, if object get keys and map on it
const map = (toIterateOver,func) => {
    return Array.isArray(toIterateOver) ? 
        toIterateOver.map(func) : 
        typeof toIterateOver === 'object' ? 
        Object.keys(toIterateOver).map(func) : 
        void(0)
}

// apply middleware func
const applyMiddleware = (middlewares, store, prop)=>{
    // copy old function
    const oldFunc = store[prop].bind(null)
    // overwrite function to apply middlewares, then, call original function with original args
    store[prop] = async function (...args){
        console.log(arguments)
        for(let middleware of middlewares){
            await middleware(...args)
        }
        oldFunc.call(null, ...args)
    }
}

// pass middlewares and stores
const createStores = helperMapFunc => (...middlewares) => (...stores) => {
    // map on stores
    helperMapFunc(stores, storeObject=>{
        // map on store props
        helperMapFunc(storeObject, prop=>{
            // if store has own prop and it is func apply middlewares
            storeObject.hasOwnProperty(prop) && typeof storeObject[prop] === 'function' && applyMiddleware(middlewares, storeObject, prop)         
        })
    })
    // return stores for usage
    return {
        ...stores
    }
}

export default createStores(map)(loggerMiddleware, otherMiddleware)(UserStore, OtherStore)

Production ready

Heya, first i want to mention that i love the library and it's very easy and fun to use it, but my question would be is anybody using it for an in production and what are the experiences with it. Also i see there is no development on the repo for a while so is this like dead, that would suck really bad.

TypeScript Support

Hey,

i would like to use this react with TypeScript and EasyState. I followed the tutorial on https://www.typescriptlang.org/docs/handbook/react-&-webpack.html, but unfortunately i don't get it to work. I've imported the easyState.js in my index.html and defined it as external in the webpack.config.js...
Actually i don't have any experiences with webpack. So maybe anyone can help me?!

Thanks in regard.

Deriving a store from props

My issue copied from Reddit.

What would be the solution to make Easy State work similar to getDerivedStateFromProps? What if I wanted a child component to update the store depending on the props passed from a parent component?

class Parent extends Component {
  store = store({ number: Math.random() });

  handleButtonClick = () => {
      this.store.number = Math.random();
  };

  render() {
      return (
          <div>
              <button onClick={this.handleButtonClick}>Generate Random Number</button>                
              <Child number={this.store.number} />
          </div>
      );
  }
}

class Child extends Component {
  store = store({
      // number needs to update based on the number prop passed from parent on will receive props
      number: this.props.number + 1,
  });

  render() {
      return <div>{this.store.number}</div>;
  }
}

How to derive a store from props on will receive props?


solkimicreb
Ouch, I didn't think about that change :D I guess I could make it work exactly like it is now with two differences.

It would get the first local store on the component as the second argument, instead of the state.
It would merge the returned object with the first local store on the component, instead of the state.
Your example would look like this:

class Child extends Component {
  store = store({
    number: this.props.number + 1
  });

  static getDerivedStateFromProps (props, store) {
    return { number: this.props.number + 1 };
  }

  render() {
    return <div>{this.store.number}</div>;
  }
}

This would place a few (tiny) restrictions on components with getDerivedStateFromProps.

These components should not have more than one local store to avoid confusion (of which store would receive the update object from getDerivedStateFromProps).
These components should not use vanilla React state in concjuction with local stores to avoid the above confusion.
Do you think this would be okay? Thanks for the catch (:

Edit: another approach would be to keep the exact current behavior with props and state and pass in a third argument, which is an array of local stores. Those stores could be mutated directly in the function, while the return value would be merged with state (as it is right now.) This feels a bit dirtier, but it would only extend the current behavior, instead of replacing some of it. Would this be better?

Edit2: I think I vouch for the first one, as the restrictions in places are also best practices. It might be beneficial to enforce them.

Extending Store in Typescript

Hi,

I am new to Typescript and this may be more appropriate question in a TS forum. However, while using react-easy-state in TS, I am not able to import its types, e.g. Store from the library. I need to extend my application's store, the use case is to define a common interface AppStore with well defined methods and properties. To achieve this, I am trying to extend the Store type and hence this issue.

Any suggestions? I thought the use case is simple enough to not warrant code, but would be glad to add some if needed.

Thanks!

TypeScript Typings, again

Issue #4 was mistakenly closed due to a misunderstanding as to what "TypeScript support" implies.

It isn't that special steps are required to use this library with TypeScript and/or WebPack. That's unrelated to what #4 was implying.

The request came about because there are no typings available so when you use this library with TypeScript, no hinting is available. This is also an issue when you have no-implicit-any enabled as that prevents you from importing the library.

Most TypeScript projects will have no-implicit-any enabled as that's largely the whole point of TypeScript -- to avoid having untyped references in your code!

Anyway -- what would be great is if this project's author created a .d.ts file and added it to this repository. The .d.ts file is kind of like a c header file that contains the definition of the signatures of any types exported by this library.

Again, issue #4 was closed in error based on a misunderstanding of both TypeScript and its ecosystem.

Hot Loader Clears Store!

Hello,

I'm using react-hot-loader on my project and I have the store objects declared outside of components.
Every time I do a small change on any of the main or child components the store clears up to default state.
Is this how it is supposed to be .. this is the same as doing a full reload.

Small example with create-react-app

import React from "react";
import { hot } from "react-hot-loader";
import { store, view } from "react-easy-state";

const storage = store({ test: 0 });

class App extends React.Component {
  componentDidMount() {
    storage.test = 1;
  }
  render() {
    console.log("App");
    return <h1>App {storage.test}</h1>;
  }
}

export default hot(module)(view(App));

When I change the header for example from App to App2 the store returns to default.

Thank You!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.