ReactJS + Redux subscribe method mess - reactjs

Using create-react-app
//index.js
...
export const store = createStore(getChange, applyMiddleware(thunk))
//getChange is my reducers name
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
// Box.js which gets rendered in App.js
import {store} from '../../../index'
...
const renderagain = () => store.getState()
store.subscribe(renderagain)
...
This throws me an error
TypeError: Cannot read property 'subscribe' of undefined
Excuse me? What am I doing wrong?

It's a circular dependency issue.
index.js imports App.js, which eventually imports Box.js. But, Box.js is attempting to import {store} from "../../index.js", which is wrong in several ways.
Your component files should not be attempting to import the store directly, and definitely should not be attempting to import values from index.js. You also shouldn't be trying to subscribe to the store directly in your component files.
Instead, use the React-Redux connect() function in your components to create wrappers that manage the process of subscribing to the store for you. Then, use the connected components, and they'll automatically use the store you passed to <Provider>.

//index.js
...
export const store = createStore(getChange, applyMiddleware(thunk))
//getChange is my reducers name
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
the code above is good to be used to create a store with some reducers combined. and also providing that tot he whole application by passing it into the Provider Component wrapping the Application. This makes the redux actions and store contents available to access for the component. There you can subscribe the store to et notified on changes in store, or on action dispatches.
You are using the callback to subscribe in a wrong way. You might use it this way:
function select(state) {
return state.some.deep.property
}
​
let currentValue
function handleChange() {
let previousValue = currentValue
currentValue = select(store.getState())
​
if (previousValue !== currentValue) {
console.log(
'Some deep nested property changed from',
previousValue,
'to',
currentValue
)
}
}
​
const unsubscribe = store.subscribe(handleChange)
unsubscribe()
For further documentation and queries you can see this. https://redux.js.org/api/store#subscribe-listener

Related

React Component with Scripts That Returns Nothing

I want to have a React component that only has functions in it. These functions should only run once and will run throughout the entirety of the app (these are bluetooth connection functions).
Usually I would create a class for this, but I need to access my Redux store (which can only happen in a component). The component would simply render <></>.
Is there a better way to do this?
You could create a custom hook and use it in your App component. Since you can run other hooks inside, you can access the store with the useStore hook and run code only once with the useEffect hook with an empty array in its dependencies.
function useBluetoothConnections() {
const store = useStore();
useEffect(() => {
// do things once
}, []);
}
function App() {
useBluetoothConnections();
return (<>
Your app here
</>)
}
const rootElement = document.getElementById('root')
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)

Using React-Redux: How to Access Variables in the Store, within Nested Components

I am using React-Redux, but I am not able to figure out how to access a variable in the Redux store inside of my nested components.
How can I share a variable between components, using React-Redux?
For example:
I have an 'index.js' file and 30 nested components. Managing these components becomes difficult after a while.
I have a 'C1.js' component. Let's just say I wrote this code in it.
function Reducer(state = 'example' , action) {
return state;
}
const store = createStore(Reducer)
index.js file:
<Provider store = {store}>
<App/>, document.getElementById('root')
</Provider>
How do I pass the 'store' variable to the 'C1.js' component to the index.js file?
Thanks...
You need to use something called "Connect" to connect your various components to the provider.
In the file that contains your C1.js component:
import {connect} from 'react-redux'
const MyComponent = () => {
let someData = props.someData
return(
//all of your JSX for your component here
)
}
const mapState = state => {
return {
someData: state.someData
}
}
export default connect(mapState)(MyComponent)
In the code above, notice the mapStateFunction. Connect is hooking that up with the Provider, and the state that is on the Provider. So that is where you are able to link whatever properties are on your Provider (React-Redux) state with this particular data.
Now, in your component, you will now have prop.someData
-
In the index file, you have your Provider in the wrong place, you need to change your code to this:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
See, the difference there? The is the React Element (and all of its children that you are asking React to render to the DOM). It is the first parameter of the ReactDOM.render function.
The second parameter to the ReactDom.render function is the element in the DOM where you want it to put all of your React elements.
You did not configure well redux and react. You need to go over the doc of redux to setup correctly. Should get working after that.

JEST testing on redux is not working as expected

I have a code in redux which i exported as
export default connect(mapStateToLinkProps, mapDispatchToLinkProps)(Link);
And in jest test case i have written to test this exported component
//imported the above component as Link
describe('Dashboard component testing', () => {
test('1. Must be an Instance of CreateAssest', () => {
const wrapper = shallow(<FilterLink />);
const inst = wrapper.instance();
expect(inst).toBeInstanceOf(Link);
});
});
For this i'm getting error
Invariant Violation: Could not find "store" in either the context or
props of "Connect(Link)". Either wrap the root component in a
, or explicitly pass "store" as a prop to "Connect(Link)".
When not using redux and exporting only as react component the test cases were working.Now in redux some store issue is coming.please can anyone guide a little in this issue what's happening
You need to wrap your component in Provider in order to be able to use store, like so:
import { Provider, connect } from "react-redux";
let store = createStore(reducer, defaultStoreItems);
<Provider store={store}>
<App />
</Provider>

Test React-Redux with mock context and actions

How can I test a Reaect-Redux component and mock its context and actions?
With this, it can't find any actions:
const wrapper = mount(
<S437TransactionAuth
store={store}
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>,
mockContext
);
This can't find mock actions or context
const wrapper = mount(
<Provider store={store}>
<S437TransactionAuth
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>
</Provider>,
mockContext
);
What is the standard practice? I'm using Mocha, for reasons beyond my control.
Our standard practice is to export both the connect component as default and the component itself as a named export.
connect is already tested for you so there's no point in verifying it's actually passing stuff from the Redux store to your component. What you can test is that your component uses that props correctly, which you can mock in your non-connected component.
You can also test your selectors and action creators you assign in mapStateToProps and mapDispatchToProps in isolation.
Last but not least, you can always simply configure your real store for the test (inside your describecalls!) and wrap your subject in a Provider with the real store. But this path could lead to you having to dispatch some actions to setup the state as your component expects it (like mocking an API call to have data, and so on).
Assuming you are using enzyme for doing the mounting, the context is supposed to be passed as
const wrapper = mount(
<S437TransactionAuth
// I'm assuming you have put your store in the context so I've removed store={store} for here
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>,
{ context: mockContext }
);
I've personally just used the react-redux Provider without doing anything with the second parameter of mount, like so
const wrapper = mount(
<Provider store={store}>
<S437TransactionAuth
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>
</Provider>
);
Also, if you're not already using it, redux-mock-store makes testing connected components much easier but allowing you to set up an initial state to render, assert expected render in the component, or simulate user actions on the component with enzyme, and assert the expected actions against the mock store.

How to access store in second component in react-redux

I have a single component App.js where I trying to save state using redux. In index.js where I set store for only <App /> component.
index.js
let store = createStore(scoreReducer);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById("root")
);
registerServiceWorker();
I have this method in App.js to map state to props which is available inside App.js.
App.js
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
Everything is well so far, now I am not sure how to access { this.props.score} in another component ?
What changes I need to do in index.js and second component if I want to access {this.props.score} in another component ?
When you are using Provider any component that is children of the Provider Higher Order Component can access the store properties though the use of connect function.
So you can add the following in any component that is a child of Provider and access the score prop
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
export default connect(mapStateToProps)(MyComponent)
However if this other component is a direct child of App component then you can also pass the score prop as a prop to this component from App like
<MyComponent score={this.props.score}/>
Provider component sets the context for all its children, providing the store in it. when you use the High Order Component(HOC) connect you can wrap any component and access the store through the provided mapStateToProps and mapStateToProps no matter how nested they are. You can also access the store using context context.store but this is not recommended. Using map functions and connect, similar to what you have with your App component, is the best approach.

Resources