Skip to content
This repository has been archived by the owner on Feb 19, 2022. It is now read-only.

Subscribing to state changes #36

Open
dannief opened this issue May 6, 2017 · 12 comments
Open

Subscribing to state changes #36

dannief opened this issue May 6, 2017 · 12 comments

Comments

@dannief
Copy link

dannief commented May 6, 2017

I am wondering if there is a plan to add a subscription or side effects api. The use case would be to execute functions on state change outside of component rendering. Example: syncing with local storage.

@xcoderzach
Copy link

I think you can you do that with middleware?

@dannief
Copy link
Author

dannief commented May 6, 2017

@xcoderzach From the documentation, with middleware, you can change state and decorate effects to add new functionality. I couldn't see how this might be used to subscribe to change states though.

However, I am now seeing from the source code and by inspecting the freactal context passed in the middleware function, that the context exposes a subscribe function. This function appears to take another function as argument that is passed the key of the state that has changed. This is exactly what I am looking for and it seems to work how I would expect.

This function isn't documented though. Is this what you were referring to?

Edit: Well not exactly what I was looking for. Based on how the middleware function currently works, it is called on every state change, which means I add a new subscription function for every state change. I can work around this by creating a variable that tracks whether I have already added the subscription, to ensure it is only added the first time a state changes , but this does seem a bit hacky. So I am unsure I am using the API as it was intended. See this related issue: #33

@zebulonj
Copy link
Contributor

zebulonj commented May 6, 2017

@dannief Middleware fires every time state changes, so it should be possible with middleware (as @xcoderzack suggests). I think you would have to be responsible for detecting what state had changed within the middleware.

The internals of freactal appear to contain machinery that does track what state has changed. Maybe those could be exposed in a way more suitable to your use case.

@drcmda
Copy link

drcmda commented May 7, 2017

That would also be a criteria for us without which we couldn't use it. We have generic classes reacting on state changes, executing actions, etc. Middleware isn't good enough and makes it very restrictive. It needs a simple getState/subscribe functionality. This will allow it to be used without components or in all frameworks and circumstances.

@divmain
Copy link
Contributor

divmain commented May 8, 2017

@drcmda, can you explain more fully what you need? Freactal is designed to be used through its higher-order components, and it is unlikely that we'll expose lower-level APIs for subscribing to state changes. However, if I could better understand the use cases you have in mind, we might be able to find a good solution, and I may change my mind if your use case is compelling :)

@divmain
Copy link
Contributor

divmain commented May 8, 2017

@dannief, the middleware is probably the piece I thought through the least. Definitely open to improvement here.

Right now, middleware is a function of signature fn(context) -> context. However, that could be changed to fn(providerInstance) -> fn(context) -> context. Each middleware function that is provided to the provideState HoC would need to return a function that transforms context when updates occur. Thoughts?

@nstadigs
Copy link

nstadigs commented May 9, 2017

You could create a react component that handles this.

import React, { Component, Children } from 'react';

import { injectState } from 'freactal';

const SyncronizeTodos = injectState(
  class TodoSyncer extends Component {
    constructor(props) {
      super(props);
    }

    componentDidMount() {
      // Subscribe to things
    }

    componentWillReceiveProps({ state }) {
      // Do something whenever local state changes
    }

    componentWillUnmount() {
      // Unsubscribe from things
    }

    render() {
      return this.props.children ? Children.only(this.props.children) : null;
    }
  }
);

export default SyncronizeTodos;

While this component is mounted in the tree it will syncronize stuff for you.

Component all the things! [INSERT MEME HERE]

@zbuttram
Copy link

I hadn't thought about it when I first looked at freactal, but it seems like the missing piece here is almost something like MobX's autorun: https://mobx.js.org/refguide/autorun.html
I don't use it very often when working with MobX but it does come in handy and I think @dannief's localStorage example is spot-on.

@dannief
Copy link
Author

dannief commented May 10, 2017

@divmain Could you elaborate a little on your thoughts for changing the signature of the middleware function?

If the middleware function returns a function that is executed on state changes, does this mean that you would (1) execute the top level middleware function once, and (2), store the resultant function to be executed on state changes?.

Also, would the providerInstance be the object that was originally passed to provideState?

Edit: I think the trouble I am having is, with the exception of needing to modify the state in a global way after every state change, I can't think of reason you would want to modify the effects or subscribe in a similar way. What I mean is, I am thinking the middleware function need only be executed once, to add subscriptions and wrap effects with other functionality. The only reason to return another function from the middleware function would be to register state changes. Or am I missing something?

@nbostrom That works. I actually like that pattern, though It may not be ideal for all use cases.

@divmain
Copy link
Contributor

divmain commented May 27, 2017

@dannief, middleware can also transform the state object itself, injecting or modifying values as desired. Because of this, something needs to be running on every state change.

Your summary of my thoughts is accurate. The outer function would be invoked once on provider construction. The inner/returned function would be invoked on every state change, much like the middleware behaves right now.

This approach could also be used to solve #43.

@divmain divmain changed the title Best way to subscribe to state changes? Subscribing to state changes May 27, 2017
@bingomanatee
Copy link

Since you can store anything in a state hash, couldn't you store a mobX observable in it and begin watching it in initialize?

@bingomanatee
Copy link

that being said - a parameter in the middleware that told us what changed since the last cycle could allow us to write much more effective middleware.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants