I am working on this project where we're using this template from MS and it's all working nice but for this particular task I am in a need for 'default' dispatch method from redux so I can focus on some form inputs and I cannot figure out how to get it.
import { ApplicationState } from '../../../store/index';
import { connect } from 'react-redux';
import * as GlobalState from '../../../store/Global';
import { Field } from 'redux-form';
import SelectHour from '../Selects/SelectHour';
import Select from '../Selects/Select';
import MultiSelectComponent from '../Selects/MultiSelectComponent';
import Days from '../Days';
import { getFormInitialValues } from 'redux-form';
import { Field, focus, blur } from 'redux-form';
import SelectHour from './SelectHour';
class WorkingDays extends React.Component<Props, State> {
...
}
export default connect(
(state: ApplicationState) => state.global,
GlobalState.actionCreators
)(WorkingDays) as typeof WorkingDays;
How can I get access to dispatch so I can use it something like this?
this.props.dispatch.focus(something, something)
dispatch is a function, so you can't make this.props.dispatch.focus(something, something) work.
But can get this.props.focus(something, something) by this way:
export default connect(
(state: ApplicationState) => state.global,
(dispath) => ({
focus: (value1, value2) => {
const action = yourAction(value1, value2)
dispath(action)
}
})
)(WorkingDays) as typeof WorkingDays;
I have never worked with MS react-redux template so I do not know what your GlobalState is look like!
Related
I'm currently working on my first real React-Redux app (ReactDom to be exact).
The app is using material ui (I'm not the project manager)
I'm having trouble applying style to a component exported via reduxForm.
my code:
import React, {Component} from 'react';
import {withStyles} from '#material-ui/core/styles/withStyles';
import { TextField } from '#material-ui/core';
import { Field, reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import { some acttions } from '../actions';
const styles = (theme) => ({});
const mapStateToProps = (state) => ({});
const mapDispatchToProps = (dispatch) => ({
some: () => dispatch(some(data)),
actions: () => dispatch(actions())
})
class FormComponent extends Component {
// Component with form
}
FormComponent = connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(FormComponent));
export default reduxForm({
form: 'myForm'
})(FormComponent);
This results in TypeError: Object(...) is not a function pointing to the command with connect. When I erase withStyle, it doesn't break, but looks bad.
Thanks in Advance,
Gal
I am struggeling on getting my first steps with Redux. All of these "Todo-App" Tutorials are nice, the "Increment the button" tutorials as well. I thought of getting my own example to teach myself the logic of Redux, but something doesnt work. At the moment, I am not sure where the state comes from, so I tried a lot of different variations to have Redux "started" without getting initialization errors, and I found a working solution! First, I just setted up the state in the reducer, but the button-describtion didnt appear. Then, I setted up the state in the store additionally, and at least the button has the decribtion test123 and the console.log worked. But how to get the state from the reducer (I checked the documentation and it was recommended to pass state by reducers, not by the store itself). At the moment, I get the following error:
Error: Objects are not valid as a React child (found: object with keys {0, 1, 2, 3}). If you meant to render a collection of children, use an array instead.
Here is my absolutely basic code which should help me understand the logic of redux:
Action type:
export const CLICK = 'CLICK'
Action Creator:
import { CLICK } from './types';
export function clicked() {
return({
type: CLICK,
payload: 'switch the describtion of the button'
})
}
the clickReducer:
import { CLICK } from '../actions/types';
const initialState = {
name: 'test'
}
export default function (state = initialState, action) {
console.log('click-test', action)
switch(action.type) {
case CLICK: {
return Object.assign({}, state)
}
default:
return state
}
}
the rootReducer:
import { combineReducers } from 'redux';
import clickReducer from './clickReducer';
export default combineReducers({
name: clickReducer
})
the store:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {
name: 'test123'
};
const middleWare = [thunk];
const store = createStore(rootReducer, initialState, applyMiddleware(...middleWare));
export default store;
and the button-component:
import React, { Component } from 'react'
import { connect } from 'react-redux';
import { clicked } from '../actions/clickAction';
class Button extends Component {
render() {
return (
<div>
<button onClick={this.props.clicked}>{this.props.name}</button>
</div>
)
}
}
const mapStateToProps = state => ({
name: state.name
});
export default connect(mapStateToProps, { clicked })(Button);
It would be very nice to get some help with this issue to be able to take further steps in Redux.
Thank you!
You don't need parentheses, do this instead:
import { CLICK } from './types';
export clicked = () => {
return {
type: CLICK,
payload: 'switch the describtion of the button'
}
}
Your "CLICK" type in the switch statement isn't updating the name, you're just returning the state. Do this, instead:
import { CLICK } from '../actions/types';
const initialState = {
name: 'test'
}
export default (state = initialState, action) => {
switch(action.type) {
case CLICK:
return {
...state,
name: action.payload
}
default:
return state
}
}
Your store has too much information, do this instead:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
export default store;
Call the object property:
import React, { Component } from 'react'
import { connect } from 'react-redux';
import { clicked } from '../actions/clickAction';
class Button extends Component {
render() {
return (
<div>
<button onClick={this.props.clicked}>{this.props.name.name}</button>
</div>
)
}
}
const mapStateToProps = state => ({
name: state.name
});
export default connect(mapStateToProps, { clicked })(Button);
this solution, regarding the reducer, still leads to the following error:
Error: Objects are not valid as a React child (found: object with keys {0, 1, 2, 3}). If you meant to render a collection of children, use an array instead.
in button (at button.js:9)
in div (at button.js:8)
in Button (created by ConnectFunction)
in ConnectFunction (at App.js:12)
in div (at App.js:11)
in Provider (at App.js:10)
in App (at src/index.js:6) react-dom.development.js:57
React 15
dispatchInteractiveEvent self-hosted:1029
I really cant imagine why it is like this, because my solution looks like okay and this is a very very primitive app to change the button description :(((
I am working on a new angular 4 plus #ngrx 4 project.
I wish to have a searching function on the loaded data.
For example, all the contacts info have been loaded in the component.
The contacts list will be filtered which contact name matched with the search text.
Please see screenshot
As the data is existed in store and I do not wish to call web api service again.
Any idea or demo code would be appreciated.
You can follow this flow to search what you need on already fetched content:
Use something like '(input)'='searchInputChange$.next(search)' in your input. So, each time the user changes the input, it will trigger our research.
Then, on your component, on the constructor, each time searchInputChange$ changes, we trigger a new SearchAction. Then, we will change our filtered contents on the reducers and the result will be inserted into contents$. On ngOnInit we just load the data from api the first time.
I'm using a model called Content, just an example, that has a string parameter title. We will use this field to filter our contents based on the search input.
import { Component, OnInit } from '#angular/core';
import { Store } from '#ngrx/store';
import { Subject } from 'rxjs/Subject';
import {of} from 'rxjs/observable/of';
/** ngrx **/
import {AppState} from '../../app-state.interface';
import * as searchActions from './actions/search.actions';
/** App Models **/
import { Content } from './models/content.model';
export class SearchComponent implements OnInit {
searchInputChange$ = new Subject<string>();
contents$: Observable<Array<Content>>;
constructor(private _store: Store<AppState>) {
this.searchInputChange$
.switchMap((text: string) => of(text))
.subscribe((text: string) => this._store.dispatch(new searchActions.SearchAction(text)));
this.contents$ = this._store.select(getSearchedContents);
}
ngOnInit() {
this._store.dispatch(new searchActions.LoadAction());
}
}
Then, we'll have our SearchActions. Load is triggered on the init of our component, fetches some contents from api. LoadSuccess is emitted on the effect of the load action in order to populate our reducer with fetched data and show it in our first component, this has a payload of an array of contents. Search will be triggered on change of our input field, this will have a string payload containing the search string.
import { Action } from '#ngrx/store';
/** App Models **/
import { Content } from '../models/content.model';
export const LOAD = '[Search] Load';
export const LOAD_SUCCESS = '[Search] Load Success';
export const SEARCH = '[Search] Search';
export class LoadAction implements Action {
readonly type = LOAD;
constructor() { }
}
export class LoadActionSuccess implements Action {
readonly type = LOAD_SUCCESS;
constructor(public payload: Content[]) { }
}
export class SearchAction implements Action {
readonly type = SEARCH;
constructor(public payload: string) {}
}
export type All
= LoadAction
| LoadActionSuccess
| SearchAction;
SearchEffect that will just fetch contents from api:
import { Injectable } from '#angular/core';
import { Actions, Effect } from '#ngrx/effects';
/** rxjs **/
import {of} from 'rxjs/observable/of';
import {map} from 'rxjs/operators/map';
import {mergeMap} from 'rxjs/operators/mergeMap';
import {catchError} from 'rxjs/operators/catchError';
/** ngrx **/
import * as searchActions from '../actions/search.actions';
/** App Services **/
import { SomeService } from '../services/some.service';
/** App Model **/
import {Content} from '../models/content.model';
#Injectable()
export class SearchEffects {
#Effect() load$ = this.actions$
.ofType(searchActions.LOAD)
.pipe(
mergeMap(() => {
return this.someService.getContentsFromApi()
.pipe(
map((contents: Content[]) => {
return new searchActions.LoadActionSuccess(contents);
}),
catchError(() => {
// do something
})
);
})
)
;
constructor(private someService: SomeService, private actions$: Actions) { }
}
SearchReducer will handle LoadSuccess when we successfully fetch contents from api and Search action that will filter our fetched contents to return only the ones containing our search string inside content's title parameter. We save first fetched contents in both of contents and searchedContents. Then, on search, we will update searchedContents to contain only contents having content.title including the searched string.
import { isEmpty } from 'lodash';
/** ngrx **/
import {createFeatureSelector} from '#ngrx/store';
import {createSelector} from '#ngrx/store';
/** App Models **/
import { Content } from '../models/content.model';
/** ngrx **/
import * as searchActions from '../actions/search.actions';
export type Action = searchActions.All;
export interface SearchsState {
contents: Content[];
searchedContents: Content[];
}
export const initialState: SearchsState = {
contents: [],
searchedContents: []
};
/ -------------------------------------------------------------------
// Selectors
// -------------------------------------------------------------------
export const selectContents = createFeatureSelector<SearchsState>('search');
export const getSearchedContents = createSelector(selectContents, (state: searchedContents) => {
return state.searchedContents;
});
export function contentsReducer(state: searchedContents = initialState, action: Action): searchedContents {
switch (action.type) {
case contentsActions.LOAD_SUCCESS:
const loadContents = action.payload.map(content => new Content(content));
return {
contents: loadContents,
searchedContents: loadContents
};
case contentsActions.SEARCH:
const keywordContents = isEmpty(action.payload) ? state.contents :
state.contents.filter(content => content.title.includes(action.payload));
return {
contents : state.contents,
searchedContents : keywordContents
};
default: {
return state;
}
}
}
So, updating searchedContents will automatically update the contents in our component.
ngrx store is the part of how you store the data. ngrx store is observable so your application flow is
Container -> Components
Container - wrapper component that will select data from store.
example:
const contacts$: Observable<contact> = this.store.pluck('contacts');
//*contacts$ - the dollar since is convention for Observable *//
Component - data visualization component, the data will be as Input(). example:
Input() contacts: Array<contact>;
this convention is called sometime SmartComponent(Container) and
DumbComponent(component)
now for a data transform/mapping you can use reactive approach(Rxjs) or functional programming or whatever you want but it not related for ngrx because in your contacts component the data as exist.
DEMO FOR YOUR SCENARIO:
contacts.container.ts
#Component({
selector: 'contacts-container',
template: `
<contacts-list [contacts]="contacts$ | async"></contacts-list>
`
})
export class ContactsContainer {
contacts$: Observable<[]contact> = this.store.pluck('contacts');
constructor(
private store: Store<applicationState>
) { }
}
contact-list.component.ts
#Component({
selector: 'contacts-list',
template: `
<input type="text" placeholder="write query" #query>
<ul>
<li *ngFor="contact of contacts | searchPipe: query.target.value">
</li>
</ul
`
})
export class ContactsListComponent {
contcats: Array<contact> = [];
constructor() { }
}
i use searchPipe for data transform ( custom pipe ) but is only example for data transform you can do it else.
Good Luck!
I'm doing a Redux tutorial, and don't understand something in it. I have the following container:
import React, { Component } from 'react';
import CommentsList from "./comments_list";
import { connect } from 'react-redux';
import * as actions from '../actions';
class CommentBox extends Component {
constructor(props) {
super(props);
this.state = { comment: '' };
}
handleChange(event) {
this.setState({ comment: event.target.value })
}
submitButton(e) {
e.preventDefault();
this.props.saveComment(this.state.comment);
this.setState({ comment: '' });
}
render () {
return(
<div>
<form onSubmit={(e) => this.submitButton(e)} className="comment-box">
<textarea
value={this.state.comment}
onChange={(e) => this.handleChange(e)} />
<button action="submit">Submit</button>
</form>
<CommentsList comment={this.state.comment}/>
</div>
);
}
}
export default connect(null, actions)(CommentBox);
This container uses:
import * as actions from '../actions';
and on the bottom of the file:
export default connect(null, actions)(CommentBox);
I'm used to using mapStateToProps and mapDispatchToProps, but here only the actions are imported, and then used in the submitButton(e) method:
this.props.saveComment(this.state.comment);
The saveComment comes from the actions/index.js file:
import { SAVE_COMMENT } from './types';
export function saveComment(comment) {
return {
type: SAVE_COMMENT,
payload: comment
}
}
Can I always use this.props to call a function from the actions/index.js file? Why don't I need to use the mapStateToProps first?
Can I always use this.props to call a function from the actions/index.js file?
Yes. From the react-redux docs:
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a dispatch call so they may be invoked directly, will be merged into the component’s props.
connect is wrapping the exports from actions/index.js with dispatch calls for you.
Why don't I need to use the mapStateToProps first?
Because mapStateToProps and mapDispatchToProps are used for different purposes and mapped separately before being merged together and injected into your component.
If either return undefined or null, they are ignored. In the case of mapStateToProps, it also means the component won't subscribe to updates from the store. Again, from the react-redux docs:
If you don't want to subscribe to store updates, pass null or undefined in place of mapStateToProps.
I'm just starting to learn Meteor with React using es6, and I'm having trouble understanding this.props. Where does it come from? I don't have any code where I define it for the class myself. Thank you in advance.
This is a portion of my code in simple-todos/imports/ui/App.jsx:
import React, { Component, PropTypes } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import ReactDOM from 'react-dom';
import { Tasks } from '../api/tasks.js';
import Task from './Task.jsx';
class App extends Component {
renderTasks() {
return this.props.tasks.map((task) => (
<Task key={task._id} task={task} />
));
}
}
This props contains the properties that are passed to the component upon creation. E.g:
<SomeComponent someProperty={'foo'} />
Now inside SomeComponent you can now access someProperty as this.props.someProperty
Read more about using props here.
It allows you to make more "arbitrary" components. For example, say you have a list that fetches from a server.
export default class List extends Component {
static propTypes = {
data: PropTypes.array.isRequired
}
render() {
return (
//you can now be sure that the proptypes are what you wanted them to be.
)
}
}
That way, you can just pass the array of data to this component
<List data={users} />