Vuex store can be manipulated when passing state to component - javascript

Not quite sure if this is a post for stackoverflow but i really struggle with this one.
I'm using vuex and vue1. I love vuex because you can't/shouldn't change the store without mutations.
But now I'm passing a state object to a (child) component. When I'm changing the value in the child component. Vuex allows me to change it.
Is there a specific reason why this can be done or is this just bad practice?
Parent component
<child-component :pass-value="stateValueParent"> </child-component>
vuex: {
getters: {
stateValueParent: (state) => state.values.stateValueParent
}
}
Child component
export default {
props: [
'passValue'
],
let value = this.passValue
// This is changed but it should right?
value = 'newValue'

Related

Accessing called component data in main template

Hopefully this is an easy fix and i can just chalk it up to my inexperience with Vue
In my main template, I can use v-for to access any objects in the templates data return and I'm successfully loading a separate component in my html
However, when I try to access info from that external component outside of my component call it doesn't show.
I'm trying to create something that I can turn into a modal which would be separate from all imported components but still be able to access data from any component in my main template's modal
How can I access data from any called component in my main template?
<template>
<!--this content shows-->
<subItem-component></subItemComponent>
<!--this doesn't-->
<p v-for="subItem in subItems>{{ subItem.title }}</p>
</template>
subitemComponent
data() {
return {
subItems: [],
}
}
You cannot access the scope of a child component within the parent. Usually, you would load the required data in the parent, if you are needing it there, and then pass it along to the child component as a prop like this:
ParentComponent:
<template>
<sub-item-component sub-item="subItem"></sub-item-component>
<p v-for="subItem in subItems>{{ subItem.title }}</p>
</template>
<script>
import SubItemComponent from '...';
export default {
name: 'ParentComponent',
components: {
SubItemComponent
},
data () {
return {
subItems: []
};
}
}
</script>
SubItemComponent:
<template>
{{ subItem }}
</template>
<script>
export default {
name: 'SubItemComponent',
props: {
subItem: {
type: Array,
required: true
}
}
}
</script>
You could also use a Vuex store to persist the data in one place. With this approach you would have access to the data from each component and do not have pass the data from component to component. To use a store like Vuex, you need to define a Vuex state and at least one mutation method to add the data.
If you want to communicate from the child up to the parent, the way to go would be by using events. You would use a listener on the child component in the parent component like this:
<sub-item-component #new-sub-item="handleNewSubItem"></sub-item-component>
You need to define a method handleNewSubItem in the parent, which is reacting each time the new-sub-item event is thrown in the child component.
The event can then be thrown within the script part of the child component like this:
const subItem = { test: 123 };
this.$emit('new-sub-item', subItem);
You don't. Your options are:
The parent component needs to manage the data and pass it down to its children via props. Child components can then subsequently emit events which the parent can listen for and modify its state.
Use a state handler outside of both the components like Vuex or Apollo (if using GraphQL).

Vuejs Mutating Object passed as a prop

If I'm passing (a reference to) an Object as a prop is it OK to mutate values in the prop?
I'm developing a web app which will require a lot of values to be passed to a component, and I'm trying to find the best way of passing the values to the component and back to the parent.
From everything I've read mutating a prop is the wrong way to do things, because next time the component is updated the values are passed back to the child component overwriting the mutations. But only the reference to the object is passed so any mutations to the values in the object prop happen to the original object in the parent component. Also Vuejs does not complain about mutating props when this happens.
const subComponent = {
name: "subComponent",
template: `
<div>
Sub Component Input
<input type="text" v-model="objectProp.val1"></input>
</div>`,
props: {
objectProp: {
required: false,
default: () => {return {val1: "carrot"}}
}
}
}
const aComponent = {
name: "aComponent",
template: `
<div>
val1: {{mainObject.val1}}
val2: {{mainObject.val2}}
<sub-component :objectProp="mainObject"></sub-component>
</div>`,
data: function() {
return{
mainObject: {
val1: "foo",
val2: "bar"
}
}
},
components: {
subComponent
}
}
new Vue({
el: "#app",
components: {
aComponent
}
})
Here is a JSFiddle showing an object prop being mutated.
JSFiddle
Is mutating a prop bad practice?
Yes absolutely. In more complex applications it is very easy to lose track of where/what/why is mutated
What is the right way to handle state across different components?
In small projects you can really do whatever you want, because you will most likely be able to follow the logic - even after not looking at the project for a year. The possibilities include:
Mutating Props (ugly but will work)
Using Events to mutate state in parent components; take a look at the EventBus (better)
Use global shared state; look at Vuex (best, but a little more boilerplate)
In big projects, however, you should absolutely use Vuex. It is a module for Vue that adds global shared state to your app, which you can access and mutate from all places in your app.
I think I understand what you are trying to do, You want to pass data to a child component, mutate it and then give that data back to the parent component.
You never want to mutate the props data given to the child component, however you CAN mutate a local state of the data, which could be an exact clone of the prop.
You can do this in many ways, I normally use a computed property as suggested in the Vue documentation:
https://v2.vuejs.org/v2/guide/components-props.html
in the computed return, just return the data coming in from the property.

update array state in flux-like

When I use flux-like like Vuex or Redux. I have a question about list state update.
I have list data in flux state.
// state.js
{
itemById: {},
}
And I have item's id array in list component.
// listComponent.js
{
data: {
itemIds: [],
items: data.itemIds.map(id => state.itemById[id]),
};
}
Now I add an item in other component.
// otherComponent.js
addItem(newItem) {
store.dispatch(newItem);
}
Then the itemById state have the newItem, but the itemIds don't have the newItem's id.
if move the itemIds to state. When the listComponent destroy and recreat, itemIds still exist, but I don't want this.
What should I do to update itemIds simply?
Complementing #blaz answer, If you are using redux, in your listComponent.js you must use the connect function from react-redux lib. It will give the possibility to load the data from your state and inject it as props in your component.
const mapStateToProps = (state) => {
const itemIds = Object.keys(state.itemById);
const items = Object.values(state.itemById);
return { itemIds, items };
};
export default connect(mapStateToProps)(ListComponent);
One problem with your code is that you have 2 copies of the same data: one in state and one in data of listComponent. Assuming listComponent's data is initiated from state's data, for every update in state, you need to catch and update local component's data to match them. Destroying and recreating the component does not directly update local data, and it's not recommended to recreate the whole component just for the local data, since it beats the purpose of component life cycle.
Flux aside, one of the reasons people use state management tool like redux or vuex is because of single source of truth - there should be one copy of global state from which any component can read. To fix your code, one solution is to move itemIds and items from data to computed and directly take them from state.
computed: {
itemIds() {
return Object.keys(state.itemByIds);
},
items() {
return Object.values(state.itemByIds);
}
}

I can't understand why components with 'connect()' are stateful in react

My question is just same as the title.
Let's say I wrote the following code.
class TODOList extends Component {
render() {
const {todos, onClick} = this.props;
return (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
onClick={onClick}
{...todo}
/>
)}
</ul>
);
}
}
const mapStateToProps = (state) => {
return {
todos: state.todos
}
}
const mapDispatchToProps = (dispatch) => {
return {
onClick(data){
dispatch(complete(data))
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(TODOList);
Now, after the last line, this code will export the TODOList component with the state as props. It's not that it contains state, but just received state and will have them as 'props', just like the method name 'mapStateToProps' explains.
In the medium post(https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) written by Dan Abramov, container component handles data as state, and presentational property do as props. Isn't it a presentational component that deals with data as props? I'm stuck with the idea that the right container should be one like below.
class CommentList extends React.Component {
this.state = { comments: [] };
componentDidMount() {
fetchSomeComments(comments =>
this.setState({ comments: comments }));
}
render() {
return (
<ul>
{this.state.comments.map(c => (
<li>{c.body}—{c.author}</li>
))}
</ul>
);
}
}
I'm not sure why react-redux named the API 'mapStateToProps', when I tried to make 'stateful'(not handling data by property) container component
First of all these guidelines are not part of the bible
you should write code that is easy to reason about for YOU and your TEAM.
I think you are missing something, A redux Container is different than a react Container.
I mean, connect will create the container for you, it doesn't mean the wraped component is a Container.
Basically you can export both versions from the same file, the Container (connected version) and the presentation version (the none connected one).
Another thing that usually throw people off, is the name of the function and argument of mapStateToProps.
I prefer the name mapStoreToProps as in
map the redux store to the component's props.
the name state can be confusing when we are in the context of react.
Edit
As a followup to your comment:
I totally didn't know these two are actually different. Could you please tell me about more details
They are different in the way that connect is creating a "Container" for you.
connect is a High Order Component that creates the Container Component for us with all the subscription logic + functions to pass portions of the store and action-creators to its children as props (mapStateToProps & mapDispatchToProps).
A "normal" Container is usually refers to a component that you write by hand, its often doesn't deal with how things should look but instead deal with certain logic of the app.
As for the other comments like
The connect HoC of react-redux just injects the properties you can request into your component. It returns a new component that is wrapped around your component so that it can update your component whenever the state you're interested in the redux store is modified
As i mentioned above, this is partially true. It's not just injecting the properties into our component, its subscribing to the store, grabbing it from the Provider (via context) and its doing all these with optimizations in mind, so we won't have to do it by ourselves.
I'm not sure how mapStateToProps can confuse someone. We are talking about a state management library
I've seen some devs that misunderstood this because react has a state and redux has a store (at least that's how it was called in most of the tutorials and documentations).
this can be confusing to some people that are new to either react or redux.
Edit 2
It was a bit confusing due to the sentence 'it doesn't mean the wraped component is a Container.' Why is the wrapped component not a container? Isn't a component created by connect also a container?
I mean that the wrapped component that you wrote doesn't have to be a Container.
You can connect a "Presentation" component:
const Link = ({ active, children, onClick }) => {
if (active) {
return <span>{children}</span>
}
return (
<a
href=""
onClick={e => {
e.preventDefault()
onClick()
}}
>
{children}
</a>
)
}
// ...
export default connect(mapState, mapDispatch)(Link)
mapStateToProps will be called when store data changes. It will pass the returned object as new props for the component. This will not affect the component's state. If you'd like to set a new state after the component got its new props you need to use another lifecycle method: static getDerivedStateFromProps (in earlier versions of react componentWillRecieveProps). The object returned by static getDerivedStateFromProps will be your new state.
https://reactjs.org/docs/state-and-lifecycle.html#adding-lifecycle-methods-to-a-class
connect() will connect your component to the redux store. Withouth the connect function (of course) your mapStateToProps will not work.
I'm not sure why react-redux named the API 'mapStateToProps'
We are talking about the store's state :)
The high level purpose is to seamlessly integrate Redux's state management into the React application. Redux revolves around the store where all the state exists. There is no way to directly modify the store except through reducers whom receive actions from action creators and for that to happen we need for an action to be dispatched from the action creator.
The connect() function directly connects our components to the Redux store by taking the state in the Redux store and mapping it into a prop.
This is power of Redux and its why we use it.
Lets say you are building a component called LaundryList and you want it to render a laundry list. After you have wired up the Provider in your "parent" component, I put it in quotes because technically Provider is a component so it becomes the parent.
You can then import the connect() function from react-redux, pass it mapStateToProps in order to get that laundry list from the Redux store into your LaundryList component.
Now that you have your list of linens inside of the LaundryList component you can start to focus on building a list of elements out of them like so:
class LaundryList extends Component {
render() {
console.log(this.props.linens);
return <div>LaundryList</div>;
}
}
That contains the list of linens object and for every list of linens inside of there we are going to return some jsx that is going to represent that linen on my list.
Back inside my laundry list component I will add a helper method inside the laundry list component called render list like so:
class LaundryList extends Component {
renderList() {
}
render() {
return <div>LaundryList</div>;
}
}
So this purpose of this helper method is to take the list of linens, map over them and return a big blob of jsx like so:
class LaundryList extends Component {
renderList() {
return this.props.linens.map((linen) => {
return (
);
});
}
render() {
return <div>LaundryList</div>;
}
}

Understanding React-Redux and mapStateToProps()

I'm trying to understand the connect method of react-redux, and the functions it takes as parameters. In particular mapStateToProps().
The way I understand it, the return value of mapStateToProps will be an object derived from state (as it lives in the store), whose keys will be passed to your target component (the component connect is applied to) as props.
This means that the state as consumed by your target component can have a wildly different structure from the state as it is stored on your store.
Q: Is this OK?
Q: Is this expected?
Q: Is this an anti-pattern?
Yes, it is correct. Its just a helper function to have a simpler way to access your state properties
Imagine you have a posts key in your App state.posts
state.posts //
/*
{
currentPostId: "",
isFetching: false,
allPosts: {}
}
*/
And component Posts
By default connect()(Posts) will make all state props available for the connected Component
const Posts = ({posts}) => (
<div>
{/* access posts.isFetching, access posts.allPosts */}
</div>
)
Now when you map the state.posts to your component it gets a bit nicer
const Posts = ({isFetching, allPosts}) => (
<div>
{/* access isFetching, allPosts directly */}
</div>
)
connect(
state => state.posts
)(Posts)
mapDispatchToProps
normally you have to write dispatch(anActionCreator())
with bindActionCreators you can do it also more easily like
connect(
state => state.posts,
dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
)(Posts)
Now you can use it in your Component
const Posts = ({isFetching, allPosts, fetchPosts, deletePost }) => (
<div>
<button onClick={() => fetchPosts()} />Fetch posts</button>
{/* access isFetching, allPosts directly */}
</div>
)
Update on actionCreators..
An example of an actionCreator: deletePost
const deletePostAction = (id) => ({
action: 'DELETE_POST',
payload: { id },
})
So, bindActionCreators will just take your actions, wrap them into dispatch call. (I didn't read the source code of redux, but the implementation might look something like this:
const bindActionCreators = (actions, dispatch) => {
return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
return actionsMap;
}, {})
}
Q: Is this ok?
A: yes
Q: Is this expected?
Yes, this is expected (if you are using react-redux).
Q: Is this an anti-pattern?
A: No, this is not an anti-pattern.
It's called "connecting" your component or "making it smart". It's by design.
It allows you to decouple your component from your state an additional time which increases the modularity of your code. It also allows you to simplify your component state as a subset of your application state which, in fact, helps you comply with the Redux pattern.
Think about it this way: a store is supposed to contain the entire state of your application.
For large applications, this could contain dozens of properties nested many layers deep.
You don't want to haul all that around on each call (expensive).
Without mapStateToProps or some analog thereof, you would be tempted to carve up your state another way to improve performance/simplify.
You got the first part right:
Yes mapStateToProps has the Store state as an argument/param (provided by react-redux::connect) and its used to link the component with certain part of the store state.
By linking I mean the object returned by mapStateToProps will be provided at construction time as props and any subsequent change will be available through componentWillReceiveProps.
If you know the Observer design pattern it's exactly that or small variation of it.
An example would help make things clearer:
import React, {
Component,
} from 'react-native';
class ItemsContainer extends Component {
constructor(props) {
super(props);
this.state = {
items: props.items, //provided by connect#mapStateToProps
filteredItems: this.filterItems(props.items, props.filters),
};
}
componentWillReceiveProps(nextProps) {
this.setState({
filteredItems: this.filterItems(this.state.items, nextProps.filters),
});
}
filterItems = (items, filters) => { /* return filtered list */ }
render() {
return (
<View>
// display the filtered items
</View>
);
}
}
module.exports = connect(
//mapStateToProps,
(state) => ({
items: state.App.Items.List,
filters: state.App.Items.Filters,
//the State.App & state.App.Items.List/Filters are reducers used as an example.
})
// mapDispatchToProps, that's another subject
)(ItemsContainer);
There can be another react component called itemsFilters that handle the display and persisting the filter state into Redux Store state, the Demo component is "listening" or "subscribed" to Redux Store state filters so whenever filters store state changes (with the help of filtersComponent) react-redux detect that there was a change and notify or "publish" all the listening/subscribed components by sending the changes to their componentWillReceiveProps which in this example will trigger a refilter of the items and refresh the display due to the fact that react state has changed.
Let me know if the example is confusing or not clear enough to provide a better explanation.
As for: This means that the state as consumed by your target component can have a wildly different structure from the state as it is stored on your store.
I didn't get the question, but just know that the react state (this.setState) is totally different from the Redux Store state!
The react state is used to handle the redraw and behavior of the react component. The react state is contained to the component exclusively.
The Redux Store state is a combination of Redux reducers states, each is responsible of managing a small portion app logic. Those reducers attributes can be accessed with the help of react-redux::connect#mapStateToProps by any component! Which make the Redux store state accessible app wide while component state is exclusive to itself.
This react & redux example is based off Mohamed Mellouki's example.
But validates using prettify and linting rules. Note that we define our props
and dispatch methods using PropTypes so that our compiler doesn't scream at us.
This example also included some lines of code that had been missing in Mohamed's
example. To use connect you will need to import it from react-redux. This
example also binds the method filterItems this will prevent scope problems in
the component. This source code has been auto formatted using JavaScript Prettify.
import React, { Component } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
class ItemsContainer extends Component {
constructor(props) {
super(props);
const { items, filters } = props;
this.state = {
items,
filteredItems: filterItems(items, filters),
};
this.filterItems = this.filterItems.bind(this);
}
componentWillReceiveProps(nextProps) {
const { itmes } = this.state;
const { filters } = nextProps;
this.setState({ filteredItems: filterItems(items, filters) });
}
filterItems = (items, filters) => {
/* return filtered list */
};
render() {
return <View>/*display the filtered items */</View>;
}
}
/*
define dispatch methods in propTypes so that they are validated.
*/
ItemsContainer.propTypes = {
items: PropTypes.array.isRequired,
filters: PropTypes.array.isRequired,
onMyAction: PropTypes.func.isRequired,
};
/*
map state to props
*/
const mapStateToProps = state => ({
items: state.App.Items.List,
filters: state.App.Items.Filters,
});
/*
connect dispatch to props so that you can call the methods from the active props scope.
The defined method `onMyAction` can be called in the scope of the componets props.
*/
const mapDispatchToProps = dispatch => ({
onMyAction: value => {
dispatch(() => console.log(`${value}`));
},
});
/* clean way of setting up the connect. */
export default connect(mapStateToProps, mapDispatchToProps)(ItemsContainer);
This example code is a good template for a starting place for your component.
React-Redux connect is used to update store for every actions.
import { connect } from 'react-redux';
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default AppContainer;
It's very simply and clearly explained in this blog.
You can clone github project or copy paste the code from that blog to understand the Redux connect.
It's a simple concept. Redux creates a ubiquitous state object (a store) from the actions in the reducers. Like a React component, this state doesn't have to be explicitly coded anywhere, but it helps developers to see a default state object in the reducer file to visualise what is happening. You import the reducer in the component to access the file. Then mapStateToProps selects only the key/value pairs in the store that its component needs. Think of it like Redux creating a global version of a React component's
this.state = ({
cats = [],
dogs = []
})
It is impossible to change the structure of the state by using mapStateToProps(). What you are doing is choosing only the store's key/value pairs that the component needs and passing in the values (from a list of key/values in the store) to the props (local keys) in your component. You do this one value at a time in a list. No structure changes can occur in the process.
P.S. The store is local state. Reducers usually also pass state along to the database with Action Creators getting into the mix, but understand this simple concept first for this specific posting.
P.P.S. It is good practice to separate the reducers into separate files for each one and only import the reducer that the component needs.
Here's an outline/boilerplate for describing the behavior of mapStateToProps:
(This is a vastly simplified implementation of what a Redux container does.)
class MyComponentContainer extends Component {
mapStateToProps(state) {
// this function is specific to this particular container
return state.foo.bar;
}
render() {
// This is how you get the current state from Redux,
// and would be identical, no mater what mapStateToProps does
const { state } = this.context.store.getState();
const props = this.mapStateToProps(state);
return <MyComponent {...this.props} {...props} />;
}
}
and next
function buildReduxContainer(ChildComponentClass, mapStateToProps) {
return class Container extends Component {
render() {
const { state } = this.context.store.getState();
const props = mapStateToProps(state);
return <ChildComponentClass {...this.props} {...props} />;
}
}
}
Yes, you can do this. You can also even process the state and return the object.
function mapStateToProps(state){
let completed = someFunction (state);
return {
completed : completed,
}
}
This would be useful if you want to shift the logic related to state from render function to outside of it.
I would like to re-structure the statement that you mentioned which is:
This means that the state as consumed by your target component can
have a wildly different structure from the state as it is stored on
your store
You can say that the state consumed by your target component has a small portion of the state that is stored on the redux store. In other words, the state consumed by your component would be the sub-set of the state of the redux store.
As far as understanding the connect() method is concerned, it's fairly simple! connect() method has the power to add new props to your component and even override existing props. It is through this connect method that we can access the state of the redux store as well which is thrown to us by the Provider. A combination of which works in your favor and you get to add the state of your redux store to the props of your component.
Above is some theory and I would suggest you look at this video once to understand the syntax better.
import React from 'react';
import {connect} from 'react-redux';
import Userlist from './Userlist';
class Userdetails extends React.Component{
render(){
return(
<div>
<p>Name : <span>{this.props.user.name}</span></p>
<p>ID : <span>{this.props.user.id}</span></p>
<p>Working : <span>{this.props.user.Working}</span></p>
<p>Age : <span>{this.props.user.age}</span></p>
</div>
);
}
}
function mapStateToProps(state){
return {
user:state.activeUser
}
}
export default connect(mapStateToProps, null)(Userdetails);

Categories