I do not want to call store.subscribe method. I just want to sync the store state with the component's state.
I am trying to make a shopping cart
- There is a component CartItems that displays SignleCartItem by passing props to SingleCartItem component using map function.
Whenever user updates the quantity SignleCartItem dispatches an action and store saves the total items added to the cart and their price.
Now the problem is that the component that displays SubTotal and Total of the cart items do not sync the state of the store with the its state ( which displays total ).
Making it simple:
One component updates the store state, other component is not syncing
it on run time.
PS: Please ignore the product detail :P
If I understand you correctly, you want to show store state 'total' in your component. You need to connect your component to the store.
An example in es6:
class SomeComponent extends React.Component{ ... }
const mapStateToProps = state => ({total:state.total});
export default connect(mapStateToProps)(SomeComponent);
doc about redux connect function is here:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
If you want to keep track of changes made in redux store, like you said in your title, you can try redux-logger. You will be able to see, in your google chrome console, every action created and every changes in your store, like this
Instead of store.subscribe() use react-redux to bind Redux with React.
In your root component, import the store you created with createStore() and provide it to a component using Provider
import App from './App.js'
import store from './store/index.js'
import {Provider} from 'react-redux'
const myApp = () => (
<Provider store={store}>
<App />
</Provider>
);
ReactDOM.render(<myApp />, document.getElementById("root"));
Then, inside any component you want to listen to the state, use connect to connect the parts of state you want in that component by mapping them to the props
import {connect} from 'redux-react'
class App extends React.Component {
render(){
return (
<div>
<h1>{this.props.title}</h1>
<div>
)
}
}
Map the state to the props before you export the component
const mapStateToProps = (state) => {
title: state.title
}
export default connect(mapStateToProps)(App)
Related
Let's say I want to have a reusable react component in my project. I also want that component to have its state under different locations without losing it during component unmount. What is the correct way to deal with this kind of architecture in React? In other words, when the user navigates between these two routes react unmounts the previous component, therefore it loads remote data on every navigation between /user and /groups routes.
I also know that there is something called Redux. I don't see a clear way how to do it using reduct. Do I need two reducers? one for Users and the other for Groups? If so it's quite inconvenient creating a new reducer and write new logic each time when I need to use ReusableComponent.
Here is a similar skeleton to describe what I am trying to do. Any hint would be helpful.
//Router example
<Router>
<Switch>
<Route exact path=”/users” >
<UserComponent>
<ReusableComponent url=”http://apidomain.com/users” />
</UserComponent>
</Route>
<Route exact path=”/groups” >
<GroupComponent>
<ReusableComponent url=”http://apidomain.com/groups” />
</GroupComponent>
</Route>
</Switch>
</Router>
//ReusableComponent Example
<ReusableComponent>
--->use url, that passed from parent component tree(users or groups) to load data and keep in state
<ReusableComponentContext>
<Head />
<Body />
<Footer />
</ReusableComponentContext>
</ReusableComponent>
EDIT
So to describe my problem better is I need to have the same component with two or more parallel state on the different locations without overriding each other. If it's possible
I would use the "React Context" api. The context wrappes your app so if one component updates/ rerenderes the state which is stored inside of the context stayes untouched. To use Context you need three files:
"UserContext" = Example => rename!
Context Component (UserContext)
import { createContext } from "react"
export const UserContext = createContext(initValue)
Parent Component (Provider)
//filename: UserContext.js
//* import React, { useState } from "react"
//* import UserContext from "./UserContext"
const [state, setState] = useState("initState")
//* return(
<UserContext.Provider value={{state, setState}}> //value="props"
<ChildComponent/>
</UserContext.Provider>
Child Component (Consumer)
//*import React, { use Context } from "react";
//*import {UserContext} from "./UserContext"
const data = useContext(UserContext) //here "UserContext"
src: short explenation of usage
Edit: consuming with a custom hook
To avoid one import-statement you can create a custom Hook like this
import React, { use Context } from "react";
import {UserContext} from "./UserContext";
const useUserContext = (()=>{
const {state, setState} = useContext(UserContext)
//use effect if you want to set the context? with the hook...
return[state, setState]
})
in your remounting component
import useUserContext from "./useUserContext"
//rfce{
const {state, setState} = useUserContext()
//}
you can connect ReusableComponent to a piece of your redux store (see connect for more details).
import { connect } from "react-redux";
const ReusableComponent = (props) => {
// some logic before return
return <div>{props.magicProperty}</div>
}
const mapStateToProps = (state) => ({ magicProperty: state.magicProperty });
return connect(mapStateToProps)(ReusableComponent);
So every time you use ReusableComponent in you app, the magicProperty is shared, You can also connect some actions to the component in order to manage that part of state in the classical redux flow.
I think I found the solution. In my case, I had some misunderstanding on what level put context provider tag in the router component tree. So in React, it's very important to put the context provider wrapper in the right location. It holds a dedicated state only for those child components that are wrapped by that context provider.
In my case, I had ReusableComponentContext inside ReusableComponent and that was the wrong approach Because everywhere I used ReusableComponent it had individual context(Therefore individual state). I moved ReusableComponentContext on the top of a couple of components to solve my problem.
I have to different functional component.
AllCourses
AddCourses
Both are two different pages.
AllCourses component displays all the courses where I used an array of object.
Now from AddCourses component where I used form to add course.
How can I update or add new course to the AllCourses from AddCourses.
I am posting from my mobile
Thanks.
If this is just a simple app for practice and dont need complex state management, then all you need to do is declare the courses state at your parent component that contains both of your All Courses and Add Courses page. This parent component could just be your app.js
Then create a function in your app.js that pushes a new course object to your courses array state. After that, pass that function to your Add Courses page as props and use it there to add a course from the child component.
You can use the props for sending data from one component to another,
here I am sending data from parent component (AllCourses.js) to child component (AddCourses.js).
AllCourses.js
import React from 'react'
import AddCourses from './AddCourses.js';
class AllCourses extends React.Component {
render(){
return(
<div>
<AddCourses course="Data from AddCourses"/>
</div>
);
}
}
export default AllCourses;
AddCourses.js
import React from 'react';
const AddCourses = (props) => {
return(
<h1> {props.message} </h2> //You can add form here and pass the value of the form
);
}
export default AddCourses;
For sending data between Child to parent or siblings to siblings you can refer to the blog
Use of Props to transfer Data between Components
I'm using React and Redux. I have a component which loads ChildComponent and depending on Redux's state will also load MainComponent
const ChooseIndex = ({ appInitMount }) => {
return (
<>
<ChildComponent />
{!appInitMount && <MainComponent />}
</>
);
};
const mapStateToProps = ({ main }) => {
return {
appInitMount: main.appInitMount
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChooseIndex);
I'm trying to write a test to check that ChildComponent is loaded:
import React from "react";
import { render } from "react-testing-library";
import ChooseIndex from "../choose-index";
test("ChooseIndex should call ChildComponent", () => {
const wrapper = render(
<ChooseIndex />
);
});
I get this error:
Error: Uncaught [Invariant Violation: Could not find "store" in either
the context or props of "Connect(ChooseIndex)". Either wrap the root
component in a , or explicitly pass "store" as a prop to
"Connect(ChooseIndex)".]
Should I mock Redux by passing an object literal to ChooseIndex? Or should I create a Redux store (as my real application does) for every test?
Try to render your component like this:
render(
<Provider store={store}>
<ChooseIndex />
</Provider>
)
And pass the actual store you use in your app. In this way, you're testing the real logic that you'll use in production. You also don't care what actions get dispatched and what's in the state. You look at what gets rendered and interact with the UI—which is what matters in the end.
Separating the component from Redux and testing the two in isolation is against the whole point of react-testing-library. You want to test your app as a real user would.
If you check out the writing tests section of the redux docs, there is an example of testing a connected component.
when you import it [A redux connected component], you're actually holding the wrapper component returned by connect(), and not the App component itself. If you want to test its interaction with Redux, this is good news: you can wrap it in a with a store created specifically for this unit test. But sometimes you want to test just the rendering of the component, without a Redux store.
In order to be able to test the App component itself without having to deal with the decorator, we recommend you to also export the undecorated component
As with most unit tests, you really want to be testing your components, and not that redux is working correctly. So the solution for you is to export both the component and the connected component, while only testing the component itself, and providing whatever props redux is passing to your component.
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)
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.
In the react-redux documentation it states that when using React-redux and connect() importing the store is not recommended. It's an anti-pattern.
http://redux.js.org/docs/faq/StoreSetup.html
Similarly, while you can reference your store instance by importing it
directly, this is not a recommended pattern in Redux. If you create a
store instance and export it from a module, it will become a
singleton. This means it will be harder to isolate a Redux app as a
component of a larger app, if this is ever necessary, or to enable
server rendering, because on the server you want to create separate
store instances for every request.
With React Redux, the wrapper classes generated by the connect()
function do actually look for props.store if it exists, but it's best
if you wrap your root component in and let
React Redux worry about passing the store down. This way components
don't need to worry about importing a store module, and isolating a
Redux app or enabling server rendering is much easier to do later.
How, then, do I access the store from any component of my choosing(even deep down in the application) once I've properly wired in the store to my app? My code properly connects App but I can't get access to the store from any child components at all. store.dispatch() is null, store.getState() is null, etc. I feel that the documentation is lacking in this regard. It's said to be magic but I'd like to know how to use the magic. Do I need to write mapDispatchToProps() again and again for every single container component? A use case would be the currentUser prop that would be available to every single child component in the application. I'd like to pass that down from App to every single child.
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);//App is now a connected component, that part is working
Inside App I have a Login component, and I'd like to dispatch an action inside it. But I need a reference to store, but apparently I'm not supposed to import it.
This is where the concept of containers come into play.
Suppose you wanted to render a Login component inside of your App. You will make a connected container.
Step 1 is to create a simple action:
const LOGIN_ATTEMPT = 'auth/LOGIN_ATTEMPT';
export const login = name => ({
type: LOGIN_ATTEMPT,
payload: { name },
});
You will now use react-redux in order to connect this action to your "presentational component". This will come through to the component as a prop.
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { login } from 'actions/auth'; // your action
import Login from 'components/auth/Login'; // your component to connect it to.
// state refers to the "current state" of your store.
const mapStateToProps = state => ({ currentUser: state.auth.user });
// dispatch refers to `store.dispatch`
const mapDispatchToProps = dispatch => {
// calling this is like calling `store.dispatch(login(...params))`
login: bindActionCreators(login, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
This connect function will take these two functions as parameters. You can now import this connected container and use it with the functions "bound" to it as properties.
Example component below.
export default class Login extends Component {
static propTypes = {
// properties below come from connect function above.
currentUser: PropTypes.shape({
name: PropTypes.string.isRequired,
}).isRequired,
login: PropTypes.func.isRequired,
};
state = { name: "" };
onChange = ({ target: { value: name } }) => this.setState({ name });
onSubmit = e => {
e.preventDefault();
login(this.state.name);
};
render() {
return (
<form onSubmit={this.onSubmit}>
<input
placeholder="name"
onChange={this.onChange}
value={this.state.name}
/>
</form>
);
}
}
Notice, you never had to reference the store.