I am new in Redux and React.
I changed data with action and they are changed !
I get to React component state. It is as follows:
How do I extract data from it and use it to render the component?
New
COMPONENT
import React from 'react';
import {connect} from 'react-redux';
import {filterChangeTab} from '../../actions/filters';
const FilterPage = React.createClass({
componentDidMount(){
const { dispatch, filterState } = this.props;
},
handleSelect: function (index, last) {
return this.props.dispatch(filterChangeTab(type[index]))
},
render() {
const {dispatch, filterState } = this.props;
return (
<div className="profile-page">
<Tabs onSelect={this.handleSelect} selectedIndex={1}></Tabs>
</div>
);
}
});
function select(state, props) {
let {
filterState
} = state;
return {
entries: filterState.entries
}
}
export default connect(select)(FilterPage);
Official documentation
The official documentation of redux is great and should guide you through your question. I would highly recommend to read through, as the following is just an excerpt.
Propagate data as props
I assume, that your store is successfully connected to your root component. Otherwise you should check the mentioned documentation again.
Also verify that you installed the react-redux bindings.
With this bindings, you could easily use the connect- API to connect your component to the redux store.
In your component you return an object with the entry entries:
return {
entries: filterState.entries
}
So you have to call this entry correctly in your render function:
import React from 'react';
import {connect} from 'react-redux';
import {filterChangeTab} from '../../actions/filters';
import Select from 'react-select';
const FilterPage = React.createClass({
componentDidMount(){
const { dispatch, filterState } = this.props;
},
handleSelect: function (index, last) {
let type = ["all", "categories", "people", "organization", "strength", "curators", "skills"];
return this.props.dispatch(filterChangeTab(type[index]))
},
render() {
//filterState.getState(); not work
// Use the correct names!
const {dispatch, entries} = this.props;
// Do whatever you want with this value
console.log(entries)
return (
<div className="profile-page">
Date range:
<Select
name="select"
value="All time"
clearable={false}
searchable={false}
options={options}
onChange={this.logChange}
/>
<Tabs onSelect={this.handleSelect} selectedIndex={1}>
</Tabs>
</div>
);
}
});
function select(state, props) {
const {filterState} = state;
const entries = filterState._root === undefined ? {} : filterState._root.entries;
return {
entries
}
}
export default connect(select)(FilterPage);
Related
I'm using react and react-redux.
I used mapstatetoprops and mapdispatchtoprops to update view of my react component.
Except re-render doesn't work after redux store changed, everything works fine. Action dispatch works fine, reducer works fine, I can console.log store state and check difference.
At first, I used useDispatch and useSelector and everything worked fine. But I'm changing it to mapdispatchtoprops and mapstatetoprops to merge my code into my project teammate's code.
I tried to put this.props.(whatineed) directly in my render()'s return in component. As I understand, through mapstatetoprops, store state should be passed into my component's props.
import React, { Component } from 'react';
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap';
import { useSelector, useDispatch } from 'react-redux';
import { checked, notchecked } from '../../../actions';
import { connect } from "react-redux";
import local from './address';
import './index.css';
const mapStateToProps = state => {
return {
localsel : state.selectedLocal.locals
}
}
let mapDispatchToProps = (dispatch) => {
return {
check: (btn) => dispatch(checked(btn)),
uncheck: (btn) => dispatch(notchecked(btn))
}
}
class Seoul extends Component {
constructor(props){
super(props)
}
render(){
var btnclicked = (e) => {
let btnname = e.target.parentNode.getAttribute('id');
if (e.target.checked) {
console.log('checked');
this.props.check(btnname);
};
if (!e.target.checked) {
console.log('not checked');
this.props.uncheck(btnname);
};
// HERE IS WHERE I CAN CHECK THE PASSED STORE STATE
console.log(this.props.localsel);
// -------------------------------------------------
}
return (
<div className='localdiv localdiv1'>
// HERE IS WHERE I WANT TO SEE MY STORE STATE
{this.props.localsel.map(val=>{
return <h1>{val}</h1>
})}
// --------------------------------------------
<ToggleButtonGroup className='togglebtngrp' type="checkbox">
<ToggleButton className='togglebtn0' onChange={btnclicked} variant="outline-secondary" value={0} id="entireseoul">Entire Seoul</ToggleButton>
{local.Seoul.map((value, index) => {
return (<ToggleButton key={index} className='togglebtn' onChange={btnclicked} variant="outline-primary" value={index + 1} id={value}>{value}</ToggleButton>)
})}
</ToggleButtonGroup>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Seoul);
this component is exported in parent component, which is
import React, { Component } from 'react';
import { Jumbotron } from 'react-bootstrap';
import { Gyeongi, Incheon, Busan, Daegue, Daejeon, Sejong, Gwangju, Ulsan, Gangwon, Gyungnam, Gyungbuk, Jeonnam, Jeonbuk, Choongnam, Choongbuk, Jeju, Othercountry } from './Locals';
import Seoul from './Locals';
import './Detailsrch.css';
class Detailsrch extends Component{
render(){
var localselect = (e) => {
let selector = document.getElementsByClassName('locals');
let selector_local = document.getElementsByClassName('localdiv');
let i = 0;
for (let j = 0; j < selector_local.length; j++) {
selector_local[j].style.display = 'none';
}
let boxclass = e.target.getAttribute('name');
if (boxclass) document.getElementsByClassName(boxclass)[0].style.display = 'block';
while (selector[i]) {
selector[i].className = 'locals';
i++;
}
if (e.target.className == 'localtext') {
e.target.parentElement.className = 'locals localclick';
} else {
e.target.className = 'locals localclick';
}
}
return (
<Jumbotron className='searchjumbo'>
<p>Locals</p>
<Seoul />
<Gyeongi />
<Incheon />
<Busan />
<Daegue />
<Daejeon />
<Sejong />
<Gwangju />
<Ulsan />
<Gangwon />
<Gyungnam />
<Gyungbuk />
<Jeonnam />
<Jeonbuk />
<Choongnam />
<Choongbuk />
<Jeju />
<Othercountry />
<hr className='firsthr' />
<p>type</p><hr />
<p>career</p><hr />
<p>country</p><hr />
<p>sex</p>
</Jumbotron>
);
}
};
export default Detailsrch;
here's my reducer
import { combineReducers } from 'redux';
const initialstate = {
locals: []
}
const localSelector = (state = initialstate, action) => {
switch(action.type){
case 'CHECKED':
if(action.payload){
var arr = state.locals;
arr.push(action.payload);
return {
...state,
locals: arr
};
} else {
return state;
}
case 'NOTCHECKED':
if(action.payload){
var arrnum = state.locals.indexOf(action.payload);
var arr = state.locals;
arr.splice(arrnum, 1);
return {
...state,
locals: arr
};
} else {
return state;
}
default:
return state;
}
};
const rootReducer = combineReducers({
selectedLocal: localSelector
});
export default rootReducer;
I expect when props value changes, component will re-render and I will see the change in the browser. Props value has changed, but nothing happens in browser.
You are mutating the redux state as below
var arr = state.locals;
arr.push(action.payload);
The redux state should be immutable. You can have a look at here for some tips on how to update the redux store in reducer.
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
I can't see Detailsrch imported in Seoul but it's vice versa Seoul is imported in Detailsrch and as per the code and comments i can see this.props.localsel is changing and this is used in Seoul so render method of Seoul will be called and since there is no mapping or usage of this.props.localsel in Detailsrch so render method will not be called.
So if you want to re-render Detailsrch you need to change the mapping of this.props.localsel from Seoul to Detailsrch and pass this value as props to Seoul it should be working.
If still issue exists please post your code to sandbox/codepen/jsfiddle so that we can reproduce.
Probably you are suffering from something like this post mentioned. One trick to make your component to get the changes from the store (When passing state value from the store as a prop) is to make a deep clone of your props (Or the prop in which you want to get the change), for this you could use JSON:
JSON.parse(JSON.stringify(propToClone));
Hope this helps.
P.S.: Don't clone props that are functions, because JSON will erase/ignore them.
This is child component as i can you Props here
Child Component:
import React from "react";
const PeopleList = props => {
console.log("child Props :", props.data);
const list = props.data.map(item => item.name);
return <React.Fragment>{"list"}</React.Fragment>;
};
export default PeopleList;
Main Component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPeople } from "../actions/peopleaction";
import PeopleName from "../containers/peopleName";
class Main extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(fetchPeople());
}
render() {
const { Error, peoples } = this.props;
console.log("data", peoples);
return (
<div className="main">
{"helo"}
<PeopleName data={peoples.results} />
</div>
);
}
}
const mapStateToProps = state => {
return {
peoples: state.peoples.peoples,
error: state.peoples.error
};
};
export default connect(mapStateToProps)(Main);
If i iterate the props multi objects array i can face Map is not define issue;
I need to iterate the props.data multi objects array in child component and i get object from Redux store. once component loaded the redux store.
can you please some one help me on this.
you can find whole code below mentioned
Try this It works in your codesandbox.
{peoples.results && <PeopleName data={peoples.results} />}
Looking at the docs for react-async-poll I'm following the Usage example to integrate asyncPoll into my component, but I'm getting a Uncaught TypeError: dispatch is not a function complaint from within my onPollinterval function
import React, { Component } from 'react';
import { connect } from 'react-redux';
import asyncPoll from 'react-async-poll';
import { fetchCaCities, } from '../actions';
import MyMap from './my-map';
class CaliforniaMap extends Component {
componentDidMount() {
this.props.fetchCaCities();
}
render() {
return (
<div>
<h1>California Map</h1>
<MyMap center={[37.5, -120]} zoom={6} layers={[this.props.caCities]} />
</div>
);
}
}
const onPollInterval = (props, dispatch) => {
console.log(dispatch); // undefined
return dispatch(fetchCaCities());
};
const mapStateToProps = state => ({
caCities: state.map.california.caCities,
});
export default asyncPoll(60 * 1000, onPollInterval)(connect(
mapStateToProps, { fetchCaCities }
)(CaliforniaMap)
Maybe react-async-poll doesn't work for connected components?
According to the docs:
The dispatch parameter is only passed to [onInterval] if it is
available in props, otherwise it will be undefined.
The example they give is confusing because it does not define dispatch anywhere, but they show onPollInterval using it.
I'm using React with Redux on my front end and using the Rails API to handle my backend. At present, I am trying to update a list of articles based on user addition of an article. The ArticleForm component fires an action creator that is successfully updating my ArticleList. However, at present the life cycle method componentWillUpdate is firing continuously making axios requests to Rails, and Rails keeps querying my database and sending back the articleList.
Note: I have tried using shouldComponentUpdate as such to no avail, the DOM doesn't update:
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
My question is: how can I use React's lifecycle methods to avoid this from happening and only happening when my articleList updates. Am I going down the wrong path using lifecycle methods? I'm fairly new to React/Redux so any and all advice is helpful!
I have the following container:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import ArticleForm from './ArticleForm'
import ArticleList from './ArticleList'
import removeArticle from '../actions/removeArticle'
import fetchArticles from '../actions/fetchArticles'
import updateArticleList from '../actions/updateArticleList'
class DumbArticleContainer extends Component {
componentWillMount() {
this.props.fetchArticles()
}
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
componentWillUpdate(newProps){
if (newProps.articleList.articleList.count !== this.props.articleList.articleList.count){
this.props.updateArticleList()
}
}
render() {
return(
<div>
<ArticleForm />
<ArticleList articleList={this.props.articleList} />
</div>
)
}
}
const ArticleContainer = connect(mapStateToProps, mapDispatchToProps)(DumbArticleContainer)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles, updateArticleList}, dispatch);
}
export default ArticleContainer
here is the ArticleForm
import React, { Component, PropTypes } from 'react'
import { reduxForm } from 'redux-form'
import addArticle from '../actions/addArticle.js'
class ArticleForm extends Component {
constructor(props) {
super(props)
this.state = {disabled: true}
}
/* Most article elements are displayed conditionally based on local state */
toggleState(){
this.setState({
disabled: !this.state.disabled
})
}
handleFormSubmit(props) {
event.preventDefault()
const {resetForm} = this.props
this.props.addArticle(props).then( ()=>{
var router = require('react-router')
router.browserHistory.push('/dashboard')
resetForm()
})
}
render() {
const disabled = this.state.disabled ? 'disabled' : ''
const hidden = this.state.disabled ? 'hidden' : ''
const {fields: {title, url}, handleSubmit} = this.props;
return (
<div className="article-form">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<button className="article-form-btn"
hidden={!hidden}
onClick={this.toggleState.bind(this)}
>
+ Add Article
</ button>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Title"
{...title}
/>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Paste Link"
{...url}
/>
{ this.state.disabled
? ''
: <input className="article-form-input"
type="submit"
value="Save"
/>
}
</form>
</div>
);
}
}
export default reduxForm({
form: 'articleForm',
fields: ['title', 'url']
},
null,
{ addArticle })(ArticleForm);
and the ArticleList
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import removeArticle from '../actions/removeArticle.js'
import fetchArticles from '../actions/fetchArticles'
import { ListGroup } from 'react-bootstrap'
import { ListGroupItem } from 'react-bootstrap'
class Article extends Component {
render(){
var articleList = this.props.articleList.articleList
return(
<div>
<ListGroup>
{ articleList.slice(articleList.length - 10, articleList.length)
.map( (article) => {
return(
<ListGroupItem href="#" header={article.attributes.title}>
{article.attributes.url}
</ListGroupItem>
)}
)}
</ListGroup>
<div> View All Articles </div>
</div>
)
}
}
const ArticleList = connect(mapStateToProps, mapDispatchToProps)(Article)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
export default ArticleList
action creator:
So here is my action creator import axios from 'axios'
import axios from 'axios'
function updateArticleList(){
const url = 'http://localhost:3000/api/v1/articles'
return axios.get(url).then( (response)=> {
return {
type: 'UPDATE_ARTICLE_LIST',
payload: response.data
}
})
}
export default updateArticleList
and reducer:
export default function articleReducer(state = {articleList: []}, action) {
switch(action.type){
case 'FETCH_ARTICLES':
return Object.assign({}, state, {articleList: action.payload.data});
case 'UPDATE_ARTICLE_LIST':
return Object.assign({}, state, {articleList: action.payload.data});
default:
return state
}
}
There is no issue with the store nor the action creators nor the reducers, they are all working pretty well. I can't really replicate the hundreds of queries rails is performing but am happy to include other code should anyone need to see it.
Thanks!
Your mapDispatchToProps is using bindActionCreators wrong. Instead of
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
you should use
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles}, dispatch);
}
bindActionCreators can, as the name suggests, bind more than one action creator.
This probably won't solve your issue but an answer is the only place I could put this nicely.
Note that you'll need to fix how you're using it as well. No more double names.
I'd like to keep a state called shouldUpdateList. Whenever I fire a action that changes the list(add or update an item to the list), I set shouldUpdateList to true. Then,set it back to false whenever I fire ajax action to fetch the list.
The lifecycle event I use to check shouldUpdateList is componentWillReceiveProps, if it's true I fire a fetch action.
EDIT: I mean keep shouldUpdateList state in Redux store. Something like:
const INIT_STATE = {
list: [],
shouldUpdateList: false
}
then
case Action.ADD_NEW:
//set shouldUpdateList to true
case Action.FETCH_LIST:
//set shouldUpdateList to false
lastly, in component
componentWillReceiveProps(nextProps) {
if(nextProps.shouldUpdateList === true) {
//dispatch action FETCH_LIST
}
}
As seen here I am trying to decouple my app's components as much as I can and make them not aware of any storage or action creator.
The goal is to have them to manage their own state and call functions to emit a change. I have been told that you do this using props.
Considering
// Menu.jsx
import React from 'react'
import { className } from './menu.scss'
import Search from 'components/search'
class Menu extends React.Component {
render () {
return (
<div className={className}>
<a href='#/'>Home</a>
<a href='#/foo'>foo</a>
<a href='#/bar'>bar</a>
<Search />
</div>
)
}
}
And
// Search.jsx
import React from 'react'
import { className } from './search.scss'
class Search extends React.Component {
render () {
let { searchTerm, onSearch } = this.props
return (
<div className={`search ${className}`}>
<p>{searchTerm}</p>
<input
type='search'
onChange={(e) => onSearch(e.target.value)}
value={searchTerm}
/>
</div>
)
}
}
Search.propTypes = {
searchTerm: React.PropTypes.string,
onSearch: React.PropTypes.function
}
export default Search
And reading here I see a smart use of Provider and connect and my implementation would look something like this:
import { bindActionCreators, connect } from 'redux'
import actions from 'actions'
function mapStateToProps (state) {
return {
searchTerm: state.searchTerm
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
dispatchSearchAction: actions.search
}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Search)
Assuming I have a store handling searchTerm as part of the global state.
Problem is, where does this code belongs to? If I put it in Search.jsx I will couple actions with the component and more important to redux.
Am I supposed to have two different versions of my component, one decoupled and one connect()ed and have <Menu /> to use it? If yes what would my files tree look like? One file per component or a like a make-all-connected.js ?
In redux, exist a new kind of component that is called containers, this is the component that use connect(mapStateToProps, mapActionsToProps), to pass the state and actions to the current component.
All depends of the use of the component. For example, if you component Search only going to be use with the same state and action, You container could be the same that your component like this:
// Search.jsx
import { connect } from 'redux'
import actions from 'actions'
import React from 'react'
import { className } from './search.scss'
class Search extends React.Component {
render () {
let { searchTerm, onSearch } = this.props
return (
<div className={`search ${className}`}>
<p>{searchTerm}</p>
<input
type='search'
onChange={(e) => onSearch(e.target.value)}
value={searchTerm}
/>
</div>
)
}
}
Search.propTypes = {
searchTerm: React.PropTypes.string,
onSearch: React.PropTypes.function
}
function mapStateToProps ({searchTerm}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
onSearch: actions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Search)
But if your plan is reuse this component in another containers and the searchTerm or the action are different on the global state. The best way is passing this properties through other containers, and keep the Search component pure. Like this:
// Container1.jsx
import { connect } from 'redux'
import actions from 'actions'
import React, { Component } from 'react'
class Container1 extends Component {
render() {
const { searchTerm, handleOnSearch } = this.props;
return (
<div>
<Search searchTerm={searchTerm} onSearch={handleOnSearch} />
</div>
)
}
}
function mapStateToProps ({interState: {searchTerm}}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
handleOnSearch: actions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Container1)
// Container2.jsx
import { connect } from 'redux'
import otherActions from 'otheractions'
import React, { Component } from 'react'
class Container2 extends Component {
render() {
const { searchTerm, handleOnSearch } = this.props;
return (
<div>
<Search searchTerm={searchTerm} onSearch={handleOnSearch} />
</div>
)
}
}
function mapStateToProps ({otherState: {searchTerm}}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
handleOnSearch: otherActions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Container2)
For more information, read the official docs about using redux with react.