Giter Site home page Giter Site logo

salvoravida / redux-first-history Goto Github PK

View Code? Open in Web Editor NEW
443.0 8.0 34.0 1.37 MB

Redux history binding support react-router - @reach/router - wouter - react-location

Home Page: https://wvst19.csb.app/

License: MIT License

JavaScript 2.61% TypeScript 97.39%
redux first history react-router reach router reachrouter react-router-redux connected withrouter

redux-first-history's Introduction

redux-first-history


Redux First History - Make Redux 100% SINGLE-AND-ONLY source of truth!

Redux history binding for

Compatible with immer - redux-immer - redux-immutable.

๐ŸŽ‰ A smaller, faster, optionated, issue-free alternative to connected-react-router

Table of Contents

Main Goal

While working with relatively large projects, it's quite common to use both redux and react-router.

So you may have components that take location from the redux store, others that take location from router context, and others from withRouter HOC.

This can generate sync issues, due to the fact that many components are updated at different times. In addition, React shallowCompare rendering optimization will not work as it should.

With redux-first-history, you can mix components that get history from wherever, they will always be tunneled to state.router.location !

Use whatever you like. History will just work as it should.

//react-router v5 - v6
useLocation() === state.router.location

//react-router v5
this.props.history.location === state.router.location
this.props.location === state.router.location
withRouter.props.location === state.router.location

//react-router v4
this.context.router.history.location === state.router.location
this.context.route.location === state.router.location

//@reach/router
this.props.location === state.router.location

//wouter - pathname
useLocation()[0] === state.router.location.pathname

Mix redux, redux-saga, react-router, @reach/router, wouter and react-location without any synchronization issue!
Why? Because there is no synchronization at all! There is only one history: reduxHistory!

  • One way data-flow
  • One unique source of truth
  • No more location issues!

Edit Redux-First Router Demo

Demo

Main Features

  • 100% one source of truth (store)
  • No synchronization depending on rendering lifecycle (ConnectedRouter)
  • No React dependency (we want history to be always in store!)
  • 100% one-way data flow (only dispatch actions!)
  • Improve React shallowCompare as there is only one "location"
  • Support react-location 3.x
  • Support react-router v4 / v5 / v6
  • Support @reach/router 1.x
  • Support wouter 2.x
  • Support mix react-router, @reach/router & wouter in the same app!
  • Fast migration from an existing project, with the same LOCATION_CHANGE and push actions (taken from RRR)
  • Handle Redux Travelling from dev tools (that's nonsense in production, but at the end of the day this decision it's up to you ...)

Installation

Using npm:

$ npm install --save redux-first-history

Or yarn:

$ yarn add redux-first-history

Usage

store.js

import { createStore, combineReducers, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import { createReduxHistoryContext, reachify } from "redux-first-history";
import { createWouterHook } from "redux-first-history/wouter";
import { createBrowserHistory } from 'history';

const { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ 
  history: createBrowserHistory(),
  //other options if needed 
});

export const store = createStore(
  combineReducers({
    router: routerReducer
    //... reducers //your reducers!
  }),
  composeWithDevTools(
    applyMiddleware(routerMiddleware)
  )
);

export const history = createReduxHistory(store);
//if you use @reach/router 
export const reachHistory = reachify(history);
//if you use wouter
export const wouterUseLocation = createWouterHook(history);

store.js (with @reduxjs/toolkit)

import { combineReducers } from "redux";
import { configureStore } from "@reduxjs/toolkit";
import { createReduxHistoryContext } from "redux-first-history";
import { createBrowserHistory } from "history";

const {
  createReduxHistory,
  routerMiddleware,
  routerReducer
} = createReduxHistoryContext({ history: createBrowserHistory() });

export const store = configureStore({
  reducer: combineReducers({
    router: routerReducer
  }),
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(routerMiddleware),
});

export const history = createReduxHistory(store);

app.js

import React, { Component } from "react";
import { Provider, connect } from "react-redux";
import { Router } from "react-router-dom";
import { store, history } from "./store";

const App = () => (
      <Provider store={store}>
        <Router history={history}>
        //.....
        </Router>
      </Provider>
    );

export default App;

app.js (react-router v6)

import React, { Component } from "react";
import { Provider } from "react-redux";
import { HistoryRouter as Router } from "redux-first-history/rr6";
import { store, history } from "./store";

const App = () => (
      <Provider store={store}>
        <Router history={history}>
        //.....
        </Router>
      </Provider>
    );

export default App;

saga.js (react-saga)

import { put } from "redux-saga/effects";
import { push } from "redux-first-history";

function* randomFunction() {
  //....
  yield put(push(YOUR_ROUTE_PATH));
  //....
}

slice.js (in a Thunk with @reduxjs/toolkit)

import { push } from "redux-first-history";

export const RandomThunk = (dispatch) => {
  //....
  dispatch(push(YOUR_ROUTE_PATH));
  //....
}
  • Just a simple Router with no more ConnectedRouter!
  • Probably, you already did connect the Redux store with react-router-redux or connected-react-router (in this case you have only to replace the import!)

Options

export const createReduxHistoryContext = ({
  history, 
  routerReducerKey = 'router', 
  reduxTravelling = false, 
  selectRouterState = null,
  savePreviousLocations = 0,
  batch = null,
  reachGlobalHistory = null
})
key optional description
history no The createBrowserHistory object - v4.x/v5.x
routerReducerKey yes if you don't like router name for reducer.
reduxTravelling yes if you want to play with redux-dev-tools :D.
selectRouterState yes custom selector for router state. With redux-immutable selectRouterState = state => state.get("router")
savePreviousLocations yes if > 0 add the key "previousLocation" to state.router, with the last N locations. [{location,action}, ...]
batch yes a batch function for batching states updates with history updates. Prevent top-down updates on react : usage import { unstable_batchedUpdates } from 'react-dom'; batch = unstable_batchedUpdates
reachGlobalHistory yes globalHistory object from @reach/router - support imperatively navigate of @reach/router - import { navigate } from '@reach/router' : usage import { globalHistory } from '@reach/router'; reachGlobalHistory = globalHistory
basename no support basename (history v5 fix)

Advanced Config

  • Support "navigate" from @reach/router
import { createReduxHistoryContext, reachify } from "redux-first-history";
import { createBrowserHistory } from 'history';
import { globalHistory } from '@reach/router';

const { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ 
  history: createBrowserHistory(),
  reachGlobalHistory: globalHistory,
  //other options if needed 
});
  • React batch updates: top-down batch updates for maximum performance. Fix also some updated edge cases.
import { createReduxHistoryContext, reachify } from "redux-first-history";
import { createBrowserHistory } from 'history';
import { unstable_batchedUpdates } from 'react-dom';

const { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ 
  history: createBrowserHistory(),
  batch: unstable_batchedUpdates,
  //other options if needed 
});

Feedback

Let me know what do you think!
Enjoy it? Star this project! :D

Credits & Inspiration

  • redux-first-routing
  • react-router-redux
  • connected-react-router

Contributors

See Contributors.

License

MIT License.

redux-first-history's People

Contributors

aalpgiray avatar albertosadoc avatar danilexx avatar dependabot[bot] avatar devzeebo avatar federico-villani avatar kurohune538 avatar maurer2 avatar phlex89 avatar salvoravida avatar sgnilreutr avatar whazor 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

redux-first-history's Issues

Reach Router Link component does not work when query string parameters are provided

Hello,

First: thanks for the library.

I realized that, with reach-router, when Link reach-router component is used with query parameters (for example <Link to="some/path?param1=value1">), the browser url is properly updated but the Route component does not change and the redux router state is not updated (pathname, hash.

Here is a sandbox that reproduce the issue (copy of the repo example, trimmed to only keep reach related code, slightly modified to display the window.location and add query params to some urls): https://codesandbox.io/s/redux-first-history-demo-01tlt

You will be able to notice that, when clicking on the Link Reach.Link to Dashboard?coucou=true#withhash:

  • the url gets updated (so window.location is fine) but the dashboard component does not get displayed
  • the store is not updated.

I cannot reproduce this issue with react-router (search is properly set in the redux store).

Also, here is a pure reach-router example that shows that it works well with query string parameters: https://codesandbox.io/s/reach-router-starter-v1-c7upk (the right component gets displayed when clicking on Link)

Note that I suspect the navigate method, generated by reachify here will not work when to param is a url with querystring parameters and a hash because only the pathname attribute is updated (might be related?).

Thank you

Add Actions union type

With redux-observable it is a pattern to have all actions as a union type, it would be nice if this library also exports an union of all possible action types. This would not require any new dependency as it is pure TypeScript.

I currently added the union typing for my project this way, but implementation would be a bit cleaner:

import {
    push,
    replace,
    go,
    goBack,
    goForward
 } from "redux-first-history";

import { locationChangeAction, back, forward } from "redux-first-history/build/es6/actions";

declare module "redux-first-history" {
    export type RouterActions = 
        ReturnType<typeof push> | 
        ReturnType<typeof replace> |
        ReturnType<typeof go> | 
        ReturnType<typeof goBack> | 
        ReturnType<typeof goForward> | 
        ReturnType<typeof locationChangeAction> | 
        ReturnType<typeof back> |
        ReturnType<typeof forward>;
}

Problem with goBack and goForward types

Hi
I have problem with usage of goBack and goForward functions. Both require argument, but probably they shouldn't. From my understanding goBack() should equal to go(-1), and goForward() to go(1). If I'm correct, then why both need an argument? What is point in having those methods?

Types for goBack and goForward

export const goBack: (to: any) => ReduxAction;
export const goForward: (to: any) => ReduxAction;

Is it possible to know if it's the first rendering?

I'm migrating from connected-react-router and it has a feature that on the first rendering, the LOCATION_CHANGE action has a property named isFirstRendering . Is there a feature similar to this?

Thanks

Accessing route paramaters

What is the best way currently to extract matched parameters from the location state? With connected-react-router we can use createMatchSelector like this:

createMatchSelector({ path: '/jobs/:id' })(state).

What is the best way to this currently with redux-first-history? Or should I use another dependency to extract params?

should we add a Link component to this repo?

The react-router Link is not usable with this library since it handles routing history.
I've created a Link component based on react-router's one: primary goal is keeping original browser handling (middle click/modifier keys)

import React from 'react'
import PropTypes from 'prop-types'

function isModifiedEvent (event) {
  return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
}

const RouterLink = React.forwardRef(({ onClick, ...rest }, forwardedRef) => {
  const { target } = rest

  const props = {
    ...rest,
    ref: forwardedRef,
    onClick: event => {
      try {
        if (onClick) onClick(event)
      } catch (ex) {
        event.preventDefault()
        throw ex
      }

      if (
        !event.defaultPrevented && // onClick prevented default
        event.button === 0 && // ignore everything but left clicks
        (!target || target === '_self') && // let browser handle "target=_blank" etc.
        !isModifiedEvent(event) // ignore clicks with modifier keys
      ) {
        event.preventDefault()
      }
    }
  }

  return <a {...props} />
})

RouterLink.propTypes = {
  onClick: PropTypes.func
}

export default RouterLink

Usage would be

<RouterLink
  href={'/my-product/1'}
  onClick={() => dispatch(push('/my-product/1'))}
/>

But I'm not sure that this is place where to put it

Remove TypeScript dependency on @reach/router

Thanks for the nice library! One thing on TypeScript: Currently the TypeScript definition file imports @reach/router which makes it a direct dependency and means I have to install that package even if I'm using react-router. It would be great if the dependency could be removed, at least from the file I have to consume :)

Support `history` 5 types

history 5.1 renamed the type LocationDescriptor to To. Since this projectโ€™s push and replace are typed like the following, their location argument is now typed as any. Current typing:

export declare const push: (location: import("history").LocationDescriptor<unknown>) => ReduxAction;

I think it can be fixed like this:

import { History, Location, Action } from 'history'
import { AnyAction as ReduxAction } from 'redux'

[...]

type To = Parameters<History['push']>[0]

declare const push: (to: To) => ReduxAction
[...]

support "navigate" from @reach-router?

I couldn't find any example how to set this up with reach router.
Can you give me a simple example how to apply the reachify history to the reach router?

Add support for React-Router v6.0.0-alpha.2

Hi,

Are you going to support the current alpha release (v2) of React-Router? Because everything seems to work but the page does not change. The action is dispatched with "@@router/LOCATION_CHANGE", pathname is correct and the browser URL changes, but it stays on the same page.

Thanks.

Question

Hi, thanks for the library! Could you say why Link component and dispatch(push(path)) work differently as when you click on Link component the LOCATION_CHANGE action reaches middlewares before react-router sees the change (and performs rendering) and when dispatching action directly everything is vice versa? I have same issue with connected-react-router. Sorry if it's wrong place for the question.

Does not work with redux-immutable

Currently I get an error when trying to use this with redux-immutable and react-router because it assumes that the redux state is an object literal instead of an Immutable collection.

What would you guys think about adding support for a selector in the options to select the router state?

const {
  createReduxHistory,
  routerMiddleware,
  routerReducer
} = createReduxHistoryContext({
  history: createHistory(),
  selectRouterState: state => state.get("router")
});

history block doesn`t work

When I want use Prompt from react-router for prevent transitions I get the Error.
its resolve by adding for reduxFirstHistory block from history lib.

Support for MemoryHistory or HashHistory

Hello,
I saw your library a couple of days ago and it was just what I was looking for. It works perfectly with redux-toolkit and react-router-dom.
However, I have a question regarding other history types. So far, you are supporting createBrowserHistory. I am trying to package my react app using electron-pack as a desktop application, and it's known that electron supports MemoryHistory and HashHistory but not BrowserHistory.
I tried using MemoryHistory and HashHistory with redux-first-history but apparently neither work. Do you think there us a way to do it or will you at least support in future versions?

Thank you

"TypeError: Cannot read property 'slice' of undefined" when setting savePreviousLocations to an integer value

Using [email protected]:

 TypeError: Cannot read property 'slice' of undefined
    at routerReducer (webpack-internal:///./node_modules/redux-first-history/lib/index.esm.js:281:60)
    at combination (webpack-internal:///./node_modules/redux/es/redux.js:538:29)

initialState.previousLocations is an empty array, but it ends up unused and the state that is passed lacks the previousLocations key, which subsequently causes this fatal error.


I'm also wondering what sort of data gets stored in previousLocations and how it relates to navigating forward(). Will previousLocations include entries that would be forward from the current location in the history stack? Maybe a silly question, but unfortunately I haven't been able to test.

We're using memoryHistory - but I don't see any reason that should make a difference here. It all seems to work until we enable savePreviousLocations.

browser-navigate-forward as compared to dispatch(push)

working on left/right router transitions.

using @reach/router and react-redux with redux-first-history as documented.

These cases work ok:

  • dispatch(push(nextLocationForward))
    • makes an action { type: '@@router/LOCATION_CHANGE', payload: { action: 'PUSH', ... } }
  • dispatch(goBack(null))
    • makes an action { type: '@@router/LOCATION_CHANGE', payload: { action: 'POP', ... } }
  • browser-navigate-back
    • makes an action { type: '@@router/LOCATION_CHANGE', payload: { action: 'POP', ... } }

In these cases I can pick the transition left or right:
transitionStyle = action.payload.action === 'POP' ? 'RIGHT' : 'LEFT'

But after pushing forward several times, and going back several times, if I hit the forward button in the browser I get a 'POP' instead of a 'PUSH'.

I tried setting the option savePreviousLocations, I didn't see any others that appeared to be on point. Something else I can try to detect the browse-forward to make it work like the 'PUSH'?

Roadmap 5.x

Roadmap 5.x

This issue is a roadmap for all work on the 5.x branch.

  • Rewrite all with TS
  • Support history v5.x
  • Support react-router v6.x
  • Deprecate/remove oldLocationChangePayload
  • Test coverage 100%
  • Better docs

if you have any ideas... now's the time!
Let's discuss.

Help creating separate apps that route together

Hi there,

I am creating a POC for having two totally separate react apps that can be nested. So there is a parent app that ha the top level router, and a sub-app with it's own router.
I'm having trouble getting the sub-app's router to link relatively - e.g. the sub app links seem to need the initial window path before it's own paths. Do you have any ideas why that might be?

The parent app is set up the same basic way as the examples.
I tried the sub app using the same setup on that too, and also without, and getting the same result.

Thanks,
Alex

add other value in the reducer & sync with window.history.location

To save scroll position on route change, I was thinking of storing scrollPos in the window.history.location.state, but this wouldn't be synced with the RFH (redux first history) reducer. And in my understanding, adding data directly to the reducer on LOCATION_STATE action wouldn't affect window.history.location.state.
The neat way would be perhaps to override default behavior RFHto add data to its reducer, how can I do that ?

Restoring scroll position is another matter and could be made directly in route component.

Support TypeScript

Please consider support typescript for a seamless integration with easy-peasy.

Don't display component in in new tab

Hi!

I'm trying to open a new tab with a different path so I could render the component related to this route in my app, but the component always renders the component I was before in the app. For example I was in a component called authors, I open an new tab and copy a path to go to books, but event though the path is for books it end up rendering authors.

How can I solve this?

react: ^17.0.2
redux: ^4.1.2
react-router-dom: ^6.0.2
history: 5.1.0

weird error, stack trace from @reach/router [Question]

I added @reach/router 1.3.4 and redux-first-history to a functional React app.

Now when I try dispatching a push action, I get the following stack

Uncaught TypeError: Cannot read property 'split' of undefined
    at pick (main.bundle.js:13228)
    at RouterImpl.render (main.bundle.js:12482)
    at finishClassComponent (main.bundle.js:59847)
    at updateClassComponent (main.bundle.js:59797)
    at beginWork (main.bundle.js:61435)
    at HTMLUnknownElement.callCallback (main.bundle.js:46307)
    at Object.invokeGuardedCallbackDev (main.bundle.js:46356)
    at invokeGuardedCallback (main.bundle.js:46418)
    at beginWork$1 (main.bundle.js:66321)
    at performUnitOfWork (main.bundle.js:65133)

The first frame pick is at @reach/router/lib/utils.js:34

I went into node_modules/@reach/router/es/lib/utils.js and put a console.log in front of that line and uri is undefined.

This is porting a working router solution to a new app, so perhaps the new app build settings or other libs are contributing, or I forgot some detail putting it altogether.

Redux looks fine before and after the push action, after the push action the router/location part of the store looks correctly updated. Along with the stack trace I get a kind of stack of React components,

  at RouterImpl (eval at ES6ProxyComponentFactory (http://localhost:3000/main.bundle.js:1:1), <anonymous>:14:7)
    at Location (http://localhost:3000/main.bundle.js:69504:20)
    at Router
    at LocationProvider (eval at ES6ProxyComponentFactory (http://localhost:3000/main.bundle.js:1:1), <anonymous>:14:7)
    at AppRouter (http://localhost:3000/main.bundle.js:69504:20)
    at div
    at Paper (http://localhost:3000/main.bundle.js:5426:23)
    at WithStyles(ForwardRef(Paper)) (http://localhost:3000/main.bundle.js:11454:31)
    at div
    at Container (http://localhost:3000/main.bundle.js:1723:23)
    at WithStyles(ForwardRef(Container)) (http://localhost:3000/main.bundle.js:11454:31)
    at div
    at ThemedAppContent (http://localhost:3000/main.bundle.js:69504:20)
    at ThemeProvider (http://localhost:3000/main.bundle.js:69504:20)
    at ThemeProvider (http://localhost:3000/main.bundle.js:69504:20)
    at Provider (http://localhost:3000/main.bundle.js:69504:20)
    at Provider (http://localhost:3000/main.bundle.js:69504:20)

I'm not sure what I might try to debug it, maybe willing to try a different routing lib instead of @reach

Issue in route if hash: null

Hello. Good library a lot of thanks.

I have one question i used library https://github.com/mjrussell/redux-auth-wrapper#readme

i have some code.

import { connectedReduxRedirect } from 'redux-auth-wrapper/history3/redirect';
import { push } from 'redux-first-history';

export const UserIsAuthenticated = connectedReduxRedirect({
  redirectPath: 'login',
  authenticatedSelector: (state) => !isEmpty(state.global.get('currentUser').toJS()),
  redirectAction: push,
  wrapperDisplayName: 'UserIsAuthenticated',
  allowRedirectBack: false,
});

but redirect to login route incorrect. I gettting loginnull instead login
I debugging code and i noticed.
image
if hash: null then i have incorrect route.
I checked if hash: undefined then all good.
Could you help me. Maybe i something understand.

Feature Suggestion: add an optional `pathnameHistory` array to the `router` state

In an app I'm building, I needed to know the previous url to properly animate certain transitions. I've been using redux-first-history for many months now (thanks for maintaining this library!) and assumed that it would have a history array within the generated reducer state, given the name of the library - but it doesn't.

I've built a little ContextProvider for my own app using the location: { pathname } from redux-first-history that simply maintains the current pathname and the previous pathname. I think redux-first-history could become more useful to more people if it simply pushed the current pathname to a pathnameHistory array in the state (optionally enabled with a flag) every time the location changed (and popped the last pathname whenever a user goes back). Seems like something very simple to implement that could be useful for particular applications. :)

Or at the very least - some equivalent of document.referrer (which doesn't really exist in the context of a Single Page App) would go a long way.

I'm happy with my own Context centric solution, but I figured I'd share this thoughts here in case it resonated with anybody else. :)

SyntaxError when importing /rr6 (Cannot use import statement outside a module)

The following code breaks when running my jest test suite:

import { Router } from 'redux-first-history/rr6'
    .../node_modules/redux-first-history/rr6/index.js:2
    import React from 'react';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      4 | import { History } from 'history'
      5 | import { FC } from 'react'
    > 6 | import { HistoryRouter as Router } from 'redux-first-history/rr6'
        | ^
      7 |
      8 | import AppWithRouterAccess from './AppWithRouterAccess'
      9 |

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
      at Object.<anonymous> (src/App.tsx:6:1)

I guess adding a package.json with the content

{"type": "module"}

to the rr6 folder should fix this.

No location change when using push from saga

Hi there,

first of all thanks for providing this package!

However, I have a small issue and I'm not sure if it my setup or anything else.
Following your docs I want to dispatch a location change from a saga (use push action creator from redux-first-history if you need to dispatch location from saga or connected components.). I'm using react-router v6.

# mysaga.ts
import { push } from "redux-first-history"
...

function* redirect() {
  yield put(push("/somewhere"))
}

function* watchSomething() {
  yield takeLatest(MyActionType.SOME_ACTION, redirect)
}

export function* mySaga() {
  yield all([
    fork(watchSomething)
  ])
}

The action is executed, but there is no redirect:

image

Why does it behave this way? Hope you can shed a light, thanks!

Here is my store setup:

const { createReduxHistory, routerMiddleware, routerReducer } =
  createReduxHistoryContext({
    history: createBrowserHistory(),
  })

const sagaMiddleware = createSagaMiddleware()
const initialState = window.INITIAL_REDUX_STATE

export const store = createStore(
  combineReducers({
    authentication: authenticationReducer,
    employees: employeesReducer,
    layout: layoutReducer,
    router: routerReducer,
  }),
  initialState,
  composeWithDevTools(
    applyMiddleware(routerMiddleware),
    applyMiddleware(sagaMiddleware)
  )
)

sagaMiddleware.run(rootSaga)

export const history = createReduxHistory(store)

setState for exact route

I have a lot of cases when I need to change state for route. in react-navigation lib we have setParams for that, in histry api we have pushState() for that. Now I use replace without router, just with new state, but it`s not a good choice.
Can you provide the method for setState and getState

Feature request: support for custom reducer function

I'm trying to upgrade our project to use this great library and I wonder if there's any plans/interest to implement an optional custom reducer function? The motivation for this would be to implement things like de-duplicating routes in the history stack, or removing routes which no longer exist.

Currently we do this via direct modifications on our history object when changing routes, but of course we won't be able to do that anymore if redux is the single source of truth.

Expose index of current history entry?

Is the index of the current location in the history stack accessible somewhere besides our un-connected history object? I don't see it in either our connected-history, nor in our Redux store. How can we determine if there are back/forward entries, and how many?

Cannot read properties of undefined (reading 'pathname'). problem when I click other Link from the page

Hello.

I am using this library, and I have problem with it.
I am using it with react-route v6.
the version of this library is 5.0.8
I have same config than in the example, but when I change between webpages, i see this:
Screen Shot 2022-03-01 at 15 40 31

my store is:

import { createStore, combineReducers, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';
import { createBrowserHistory } from 'history';
import { createReduxHistoryContext } from 'redux-first-history';
import { redesign } from './redesign/redux/reducers';
import { v04 } from './redesign/redux/reducers/v04';
import sagas from './sagas';

const sagaMiddleware = createSagaMiddleware();
const historyApi = createBrowserHistory();

const { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({
  history: historyApi,
  savePreviousLocations: 10
});

export const store = createStore(
  combineReducers({
    router: routerReducer,
    redesign,
    v04
  }),
  composeWithDevTools(
    applyMiddleware(sagaMiddleware),
    applyMiddleware(routerMiddleware)
  )
);

sagaMiddleware.run(sagas);
export const history = createReduxHistory(store);

The router is like:

import { history } from './store';
import { HistoryRouter as Router } from 'redux-first-history/rr6';

const App = () => {
  return (
   <Router history={history}>
      ...
   </Router>
  );
}

And inside the routes:

const component = () => {
  ...
  return (
    <Routes>
      <Route path="/" element={<Navigate to="/homepage" replace />} />
      <Route path="homepage" element={<StartPage />} />
      ...
      <Route path="*" element={<NotFound />} />
  );
}

I am think I am skiping something easy and short. but I cannot fin it.
This is happening when I am updating to v6 of react and also this library to last version.
After error, if I reaload the page, page works.
Also, I am looking in the redux, the state of path doesnt change. Before yes.

Thanks for your time.

Kind regards.

make actions aware of basename

Thanks for this great work !

HistoryRouter allow to set a base name like this :

import { HistoryRouter as ReactRouter } from 'redux-first-history/rr6'

<ReactRouter history={history} basename="someBasePath"}>
...

But actions like (push, replace, go ...) are not aware of the basename so for exemple if I call push("site"), I go to /site instead of /someBasePath/site

Of course I can extract the basePath from the Uri, but I think my request makes sense.

reach-router: `search` store attribute not updated with `Reach.Link`

Hello,

When a Reach.Link that contains querystring parameters is clicked:

  • pathname string contains the path with the querystring (search) params
  • the search remains empty

That issue can be seen here: https://codesandbox.io/s/redux-first-history-demo-56jrr .

I suspect that it's because the navigate() method, generated by reachify here does not work when to param is a url with querystring parameters because only the pathname attribute is updated.

navigate on @reach/router not working

Based on the default configuration if you trying to navigate programatically you wont be able to do it.

Here the example code

import React from "react";
import { Link, Router, navigate } from "@reach/router";
import { useDispatch } from "react-redux";
import { initialize } from "store/actions";

import Posts from "containers/Posts";
import Users from "containers/Users";

function Title() {
  return <h1>App</h1>;
}

export default function App() {
  const dispatch = useDispatch();

  React.useEffect(() => {
    dispatch(initialize());
  }, [dispatch]);

  const handleClick = destination => {
    dispatch(() => navigate(destination));
  };

  return (
    <>
      <Title />
      <Link to="/">Home</Link> / <Link to="users">Users</Link> /
      <button onClick={() => handleClick("/posts")}>Posts</button>
      <Router>
        <Posts path="/posts" />
        <Users path="/users" />
      </Router>
    </>
  );
}

GA release

When can we expect a GA release of this component?

Everything here seems very stable and reliable, so I'd love to see the package version reflect that. I'm excited to use it, and even more eager to use it without the alpha qualifier.

Thanks!

Uncaught TypeError

Using react router 6 and i have skipLibCheck true in my tsconfig. Why is this error showing up for me?

create.ts:183 Uncaught TypeError: Cannot read properties of undefined (reading 'location')
    at Object.get location [as location] (create.ts:183:1)
    at Module../src/redux/app.store.ts (app.store.ts:21:1)
    at Module.options.factory (react refresh:6:1)
    at __webpack_require__ (bootstrap:24:1)
    at fn (hot module replacement:61:1)
    at Module../src/index.tsx (index.ts:2:1)
    at Module.options.factory (react refresh:6:1)
    at __webpack_require__ (bootstrap:24:1)
    at startup:7:1
    at startup:7:1

Seem to point to https://github.com/salvoravida/redux-first-history/blob/master/src/create.ts#L183?

[Question] Is it possible to use hooks from react-router package?

Hi @salvoravida

Thanks for your great wrapper and quick support of the rr6.

  1. In rr we have some hooks for using, can I use these hooks in my project (redux) or I should create my own hook where I should call push method from you wrapper?
  2. If I wanna to do anything I should touch your api methods first for the correct work of the rr and wrapper?

Thanks

Push not updating use params (react router v6)

Thank you for this package, I'm running into something silly, I am wondering if I miss something obvious.

It seems that if I use dispatch(push('route/with-param')) useParams is not getting updated.

My route looks like <Route path="/home/:tab" element={<HomePage />} />

And then I have two components;

Here I need the value of the param:

import HomeTabs from 'Components/HomeTabs';
import React, { Component, useEffect, useState } from 'react';
import { useParams } from 'react-router';

const HomePage = ( ) => {
  const p = useParams(); // not receiving new value for `tab`

  return (
    <>
      <HomeTabs />
      <div>This is home {p.tab}</div>
    </>
  );
}

export default HomePage;

Here is the navigation:

import React, { Component, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import { push } from 'redux-first-history';
import { useLocation, useParams, useNavigate } from "react-router-dom";

import NavTabs from 'Presentation/Tab';

const tabs = [{
  value: "tab1",
  label: "1"
},{
  value: "tab2",
  label: "2"
}];

const allowedTabValues = tabs.map(({value}) => value);

const HomeTabs = () => {
  const navigate = useNavigate();
  const dispatch = useDispatch();
  const {tab} = useParams();

  useEffect(() => {
    if(allowedTabValues.indexOf(tab) < 0){
      const url = `/home/tab1`
      dispatch(push(url));
      // navigate(url); // -> I need this to make it work
    }
  }, [tab]);

  const handleChangeTab = (e, value) => {
    const url = `/home/${value}`
    dispatch(push(url));
    // navigate(url); // -> I need this to make it work
  };

  return (
      <NavTabs tabs={tabs} value={tab} onChange={handleChangeTab}/>
  );
}

export default HomeTabs;

Does anyone know what I am doing wrong? Or is this a bug?

warning with source-map-loader

npm packages is missing src folder so build gives warning

WARNING in ./node_modules/redux-first-history/build/es6/reducer.js
Module Warning (from ../../node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map from '.../node_modules/redux-first-history/src/reducer.ts' file:
Error: ENOENT: no such file or directory, open '.../node_modules/redux-first-history/src/reducer.ts'
 @ ./node_modules/redux-first-history/build/es6/create.js 3:0-48 17:26-45
 @ ./node_modules/redux-first-history/build/es6/index.js 3:0-53 3:0-53
 @ ./src/resources/store.ts 5:0-64 27:28-53
 @ ./src/components/with-global-store.tsx 7:0-49 13:15-20 15:17-24
 @ ./src/index.tsx 17:0-65 19:19-34

workaround is it to exclude from loading

            {
                test: /\.js$/,
                enforce: 'pre',
                use: ['source-map-loader'],
                exclude: [
                    /node_modules\/redux-first-history/
                ]
            },

[Question] How to use this with separate root reducer file?

I have my reducers in a separate file, which has the combineReducers function.

I need the routerReducer in separate file and createReduxHistory, routerMiddleware in store.

If I make 2 calls to createReduxHistoryContext, won't it create different instances of history?

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.