117

I am just making a simple app to learn async with redux. I have gotten everything working, now I just want to display the actual state onto the web-page. Now, how do I actually access the store's state in the render method?

Here is my code (everything is in one page because I'm just learning):

const initialState = {
        fetching: false,
        fetched: false,
        items: [],
        error: null
    }

const reducer = (state=initialState, action) => {
    switch (action.type) {
        case "REQUEST_PENDING": {
            return {...state, fetching: true};
        }
        case "REQUEST_FULFILLED": {
            return {
                ...state,
                fetching: false,
                fetched: true,
                items: action.payload
            }
        }
        case "REQUEST_REJECTED": {
            return {...state, fetching: false, error: action.payload}   
        }
        default: 
            return state;
    }
};

const middleware = applyMiddleware(promise(), thunk, logger());
const store = createStore(reducer, middleware);

store.dispatch({
    type: "REQUEST",
    payload: fetch('http://localhost:8000/list').then((res)=>res.json())
});

store.dispatch({
    type: "REQUEST",
    payload: fetch('http://localhost:8000/list').then((res)=>res.json())
});

render(
    <Provider store={store}>
        <div>
            { this.props.items.map((item) => <p> {item.title} </p> )}
        </div>
    </Provider>,
    document.getElementById('app')
);

So, in the render method of the state I want to list out all the item.title from the store.

Thanks

micha_s
  • 73
  • 7
Parkicism
  • 2,415
  • 5
  • 20
  • 21
  • 5
    You are almost there. You need to create a store connected component using the `react-redux` library. I highly recommend you polish your understanding of redux with the free course by the author: https://egghead.io/courses/getting-started-with-redux – ctrlplusb Jul 12 '16 at 15:24
  • 3
    You do `store.getState()` to actually read the state from your store. http://redux.js.org/docs/api/Store.html#getState – Kenny Worden Jul 12 '16 at 15:30
  • 2
    Thanks for the tutorial. I don't fully understand redux and this tutorial will help me out a lot. – Parkicism Jul 12 '16 at 15:32

8 Answers8

85

You should create separate component, which will be listening to state changes and updating on every state change:

import store from '../reducers/store';

class Items extends Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
    };

    store.subscribe(() => {
      // When state will be updated(in our case, when items will be fetched), 
      // we will update local component state and force component to rerender 
      // with new data.

      this.setState({
        items: store.getState().items;
      });
    });
  }

  render() {
    return (
      <div>
        {this.state.items.map((item) => <p> {item.title} </p> )}
      </div>
    );
  }
};

render(<Items />, document.getElementById('app'));
Phil Hudson
  • 3,819
  • 8
  • 35
  • 59
1ven
  • 6,776
  • 1
  • 25
  • 39
  • 66
    @1ven how to we get `store` variable defined here? – Bang Dao Jan 04 '17 at 03:57
  • 3
    @BangDao we can assume, that we are importing it from external file. `store` variable - it is redux store instance. – 1ven Jan 05 '17 at 10:02
  • 32
    @BangDao You should include the `store` import for clarity. – Clay Banks Mar 06 '18 at 20:44
  • 5
    ReferenceError Can't find variable: store – Pete Alvin Mar 24 '18 at 13:56
  • 1
    @PeteAlvin you need to have a redux store variable defined in your scope. – 1ven Mar 25 '18 at 16:20
  • Aren't we supposed to access the reducer for that file instead of the entire store? Just access the itemsReducer instead to limit the state scope provided to items class – devssh Jul 30 '18 at 05:09
  • 4
    `import store from '../reducers/store';` . and `store.js` would contain `const createStoreWithMiddleware = applyMiddleware(thunkMiddleware,promise)(createStore); export default createStoreWithMiddleware(reducers);` – Anjan Biswas Oct 31 '18 at 17:58
  • 1
    @1ven or you could have added that line to your answer, much better than assuming – Caio Mar Jul 05 '19 at 07:29
  • @Annjawn can you provide a answer? 'applyMiddleware' is not defined no-undef 'thunkMiddleware' is not defined no-undef 'promise' is not defined no-undef 'createStore' is not defined no-undef 'reducers' is not defined no-undef – staminna Jan 12 '20 at 21:45
56

Import connect from react-redux and use it to connect the component with the state connect(mapStates,mapDispatch)(component)

import React from "react";
import { connect } from "react-redux";


const MyComponent = (props) => {
    return (
      <div>
        <h1>{props.title}</h1>
      </div>
    );
  }
}

Finally you need to map the states to the props to access them with this.props

const mapStateToProps = state => {
  return {
    title: state.title
  };
};
export default connect(mapStateToProps)(MyComponent);

Only the states that you map will be accessible via props

Check out this answer: https://stackoverflow.com/a/36214059/4040563

For further reading : https://medium.com/@atomarranger/redux-mapstatetoprops-and-mapdispatchtoprops-shorthand-67d6cd78f132

Community
  • 1
  • 1
Zakher Masri
  • 1,097
  • 1
  • 16
  • 19
  • 5
    NOTE: This way, the props won't be accessible without invoking the action (defined in `mapDispatchToProps`). If you are attempting to obtain what's already in the store without dispatching another fetch cycle then you would have to use either `subscribe` or `getState` on the `store`. – Anjan Biswas Oct 31 '18 at 18:04
24

All of the answers are from pre-hook era. You should use useSelector-hook to get the state from redux.

In your redux-reducer file or somewhere where you can import it easily:

import { useSelector } from 'react-redux'

export function useEmployees() {
  return useSelector((state) => state.employees)
}

In your application code:

const { employees } = useEmployees()

More information on redux-hooks: https://react-redux.js.org/api/hooks to accomplish this goal.

Ilmari Kumpula
  • 831
  • 1
  • 10
  • 18
  • 1
    Will this hook listen to the changes in the store? Will it update the local variable and re-render the component every time the store is changed? – Ibrahim Farooq Apr 26 '21 at 00:36
  • @IbrahimFarooq, yes, it does. It functions like a passed prop. – Ilmari Kumpula Apr 26 '21 at 11:05
  • 2
    Why we need another hook over useSelector()? ```const employees = useSelector( state => state.employees)``` – Yogi May 08 '21 at 04:44
  • 2
    @Yogi, you can totally use it without the extra-hook. I personally prefer to keep the Redux-specific useSelector only under Redux-folders. Secondary reason is that I find the useEmployees to be more expressive – Ilmari Kumpula May 09 '21 at 17:58
  • 1
    @IlmariKumpula Thank you for the explanation! Thats good format to keep useSelector call functions under redux folder. – Yogi May 11 '21 at 01:48
  • @IlmariKumpula you don't need to do anything in your reducer, you can simply `import {useSelector} from react-redux` in any functional component and access the state directly: `const employees = useSelector((state) => state.employees)`. – Emile ASTIH May 25 '22 at 13:05
  • Very good idea, you might add that this solution only works in `Component` context. Otherwise we must use `store.getState()`. – Lounis May 04 '23 at 11:54
21

You need to use Store.getState() to get current state of your Store.

For more information about getState() watch this short video.

benomatis
  • 5,536
  • 7
  • 36
  • 59
semanser
  • 2,310
  • 2
  • 15
  • 33
  • So do I just change `this.props.items` to `store.getState().items`? When I did it, it's not outputting the `item.title`. – Parkicism Jul 12 '16 at 15:33
  • 1
    @Parkicism looks like you do not did initial rendering for your component. I highly recommend you to watch this course: https://egghead.io/courses/getting-started-with-redux – semanser Jul 12 '16 at 15:41
  • 1
    @Parkicism this not displaying items, because when app is rendering first time, response from server is not received yet, you need to subscribe to store, to update component each time store changes. – 1ven Jul 12 '16 at 15:47
  • I have `let store = createStore(todoApp);` in `index.js` and I want to access the `store` inside `App.js` - What is the way of it ? – N Sharma Jun 14 '17 at 15:01
  • TypeError: undefined is not an object (evaluating '_redux.Store.getState') – Pete Alvin Mar 24 '18 at 14:01
  • @PeteAlvin Store is not a member of Redux variable. You should use store received from createStore(), like in this example: https://redux.js.org/api-reference/store#example – semanser Mar 24 '18 at 14:05
  • @semanser I'm trying to access the store in a view that's in an external .js file (not in the main App.js file where I call createStore()). So, I'm guessing I need to put the store in a "global object" that is shared across all views? – Pete Alvin Mar 24 '18 at 14:10
  • @semanser I thought the purpose of was to be able to access the store from views, but I can't find any examples of how to actually retrieve it from a view in a separate file. – Pete Alvin Mar 24 '18 at 14:12
  • I agree, in my case useSelector was fetching stale data as compared to store.getState() . state changes were dispatched and called from the same component. – Anil_M Oct 04 '22 at 23:17
6

You want to do more than just getState. You want to react to changes in the store.

If you aren't using react-redux, you can do this:

function rerender() {
    const state = store.getState();
    render(
        <div>
            { state.items.map((item) => <p> {item.title} </p> )}
        </div>,
        document.getElementById('app')
    );
}

// subscribe to store
store.subscribe(rerender);

// do initial render
rerender();

// dispatch more actions and view will update

But better is to use react-redux. In this case you use the Provider like you mentioned, but then use connect to connect your component to the store.

Brandon
  • 38,310
  • 8
  • 82
  • 87
5

If you want to do some high-powered debugging, you can subscribe to every change of the state and pause the app to see what's going on in detail as follows.

store.js
store.subscribe( () => {
  console.log('state\n', store.getState());
  debugger;
});

Place that in the file where you do createStore.

To copy the state object from the console to the clipboard, follow these steps:

  1. Right-click an object in Chrome's console and select Store as Global Variable from the context menu. It will return something like temp1 as the variable name.

  2. Chrome also has a copy() method, so copy(temp1) in the console should copy that object to your clipboard.

https://stackoverflow.com/a/25140576

https://scottwhittaker.net/chrome-devtools/2016/02/29/chrome-devtools-copy-object.html

You can view the object in a json viewer like this one: http://jsonviewer.stack.hu/

You can compare two json objects here: http://www.jsondiff.com/

Let Me Tink About It
  • 15,156
  • 21
  • 98
  • 207
1

HACK SOLUTION: Example from my REAL project! Save Redux store objects to external JSON file.

STEP-1 import useStore first from react-redux and then getState() function is used to access store state.

STEP-2 area is the name of my slice in Redux store and areaName is state in that slice.

STEP-3 FiletoSave variable is used to export JSON file with data from store.

import { useStore } from "react-redux";

const exportJsonFileFromStore = () => {

const store = useStore();
const FileSaver = require('file-saver');    
    
function exportData() {
   
    const filename = 'filter_settings';

    let prepareObject = {       // It is used to make a JSON object 
      areaName:store.getState().area.areaName  ,   
    }
    const fileToSave = new Blob([JSON.stringify(prepareObject)], {
        type: 'application/json'
    });
    // this will save file
    FileSaver.saveAs(fileToSave, filename);
}

return (
  
        <button onClick={(event: any) => exportData()}>Click me to download!</button>
   
)

}

Jamal Ashraf
  • 569
  • 6
  • 9
0
import { ReactReduxContext } from 'react-redux';
var store  = useContext(ReactReduxContext).store.getState();

console.log(store);

https://react-redux.js.org/using-react-redux/accessing-store#using-reactreduxcontext-directly

Harsh Mangalam
  • 1,116
  • 1
  • 10
  • 19
Sefidani
  • 1
  • 1