0
votes

I have three pages, PageA, PageB and PageC, that contain a form element formField.

State in globalReducer.js

import { fromJS } from 'immutable';

const initialState = fromJS({
  userInteractionBegun: false,
  pageActive: '',
  refreshData: true,
})

I want to dispatch an action that sets pageActive to corresponding page value(One of A, B or C) when the component(page) mounts and refreshes formField to blank if userInteractionBegun === false.

For every page component, to get pageActive state in props from globalReducer, I do,

function PageA(props) {
  //.....
}

// globalState is defined in conigureStore, I am using immutable.js. Link provided below this code.
const mapStateToProps = state => ({
  pageActive: state.getIn(['globalState', 'pageActive']),
})

export default connect(mapStateToProps, null)(PageA);

Link to immutable.js getIn()

store.js

import globalReducer from 'path/to/globalReducer';

const store = createStore(
  combineReducers({
    globalState: globalReducer,
    //...other reducers
  })
)

I want to abstract the logic to update pageActive every time a component(page) mounts.

I know how to abstract this logic using an HOC, but I don't know how to do it using react hooks, so that every time pageA, pageB or pageC mounts, an action to setPageActive is dispatched and formField is set to blank if userInteractionBegun is false.


For instance, I would do in pageA.js

import usePageActive from 'path/to/usePageActive';

const [pageActive, setPageActive] = useReducer(props.pageActive);
usePageActive(pageActive);

Then in usePageActive.js

export default usePageActive(pageActive) {
  const [state, setState] = useState(pageActive);
  setState(// dispatch an action //)
}
1
Can you use an effect? "If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined." -- reactjs.org/docs/hooks-effect.html - richbai90
@richbai90 Umm, I am want to usePageActive custom effect. I am not sure of how to dispatch an action from there. - HarshvardhanSharma
I apologize I didn't understand your question at first. I think that my updated answer will be helpful to you. - richbai90

1 Answers

0
votes

I haven't had much time to dip my toes into react hooks yet, but after reading the docs and playing with it for a minute, I think this will do what you're asking. I'm using built-in state here, but you could use redux or whatever else you like in the effect. You can see a working example of this code here The trick is using a hook creator to create the custom hook. That way the parent and children can keep a reference to the same state without the useEffect affecting the parent.

import React, { useState, useEffect } from 'react';
import ReactDOM from "react-dom";

const activePageFactory = (setActivePage) => (activePage) => {
  useEffect(() => {
    setActivePage(activePage)
    return () => {
      setActivePage('')
    }
  }, [activePage])
  return activePage
}

function App() {
  const [activePage, setActivePage] = useState('');
  const [localPage, setLocalPage] = useState('Not Selected');
  const selectedPage = () => {
    switch(localPage) {
      case 'A':
        return <PageA useActivePage={activePageFactory(setActivePage)}  />
      case 'B':
        return <PageB useActivePage={activePageFactory(setActivePage)}  />
      default:
        return null;
    }
  }
  return (
    <div>
      <p>Active page is {activePage}</p>
      <button onClick={() => setLocalPage('A')}>
        Make A Active
      </button>
      <button onClick={() => setLocalPage('B')}>
        Make B Active
      </button>
      {
        selectedPage()
      }
    </div>
  );
}

function PageA({useActivePage}) {
  useActivePage('A');
  return (
    <div>
      <p>I am Page A</p>
    </div>
  )
}

function PageB({useActivePage}) {
  useActivePage('B');
  return (
    <div>
      <p>I am Page B</p>
    </div>
  )
}