I apologize for such a newbie question, but how I can realize switching between tabs using react-redux. How to reproduce this logic using the storage My inital code:
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = { tab: 0 };
}
switchingTabs = (event, value) => {
this.setState({
tab: value,
});
};
render() {
return (
<div className="App">
<Container>
<AppBar className="bar" position="static">
<Tabs value={this.state.tab} onChange={this.switchingTabs}>
<Tab label="Commits Info"></Tab>
<Tab label="User info"></Tab>
</Tabs>
</AppBar>
<TabBackbone value={this.state.tab} index={0}></TabBackbone>
<TabBackbone value={this.state.tab} index={1}></TabBackbone>
<Content></Content>
</Container>
</div>
);
}
Index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
I tried to do it myself, but I don't know how to export value to reducer
It sounds like you're just getting started with Redux, which is great!
You need a state that will represent your tab selection:
export interface IState {
tabNumber : number
}
export initialState : IState = {
tabNumber : 0
}
You are going to need to define a reducer:
export function appReducer(state=initialState, action : AnyAction) : IState{
// state reduction logic here
}
Then create a store:
export const store = createStore(appReducer); // import {createStore} from "redux";
Then wrap your application or portion thereof in a Provider that takes your store:
<Provider store={store}> {/* import {Provider} from "react-redux"; */}
<App/>
</Provider>
That said, I would consider whether you need Redux. I typically use the React Context API, especially in situations like this where the context is intended to essentially perform the function of a higher-order function.
Related
Just trying to build a simple react-redux code here. Whenever I click one of the button with some data value attached to it, I just want it to use it to update state of my react app.
The directory composition is :
src/index.js
src/App.js
src/reduxButtonGroup.js
src/reduxActions.js
src/reducers/index.js
src/reduxStore.js
src/reduxHelloWorld.js
Now, coming to the codes here is how App.js looks like:
import React from 'react';
import HelloWorld from './reduxHelloWorld.js';
import {store} from './reduxStore.js'
import ButtonGroup from './reduxButtonGroup.js';
import 'bootstrap/dist/css/bootstrap.min.css';
class App extends React.Component {
render(){
return(
<React.Fragment>
<HelloWorld name = {store.getState().name} />
<ButtonGroup names = {["NameA","NameB","NameC"]}/>
</React.Fragment>
)
}
}
export default App;
Here is how reduxButtonGroup.js looks like:
import React from 'react';
import Button from 'react-bootstrap/Button';
import {store} from './reduxStore.js';
import {setName} from './reduxActions.js';
const ButtonGroup = ( {names} ) => (
<div>
{names.map((name,i) => (
<Button data-name={name} key={`btn-${i}`} style={{marginRight: "15px"}} className={"btn btn-danger"} onClick={dispatchBtnAction}>
{name}
</Button>
))}
</div>
);
function dispatchBtnAction(e){
const name = e.target.dataset.name;
console.log(name);
store.dispatch(setName(name));
}
export default ButtonGroup;
And reduxActions.js:
export function setName (name) {
return {
type: "SET_NAME",
name: name
}
}
Also, reducers/index.js:
export default (state,action) => {
console.log(action.name)
switch(action.type){
case "SET_NAME":
console.log("here")
return{
...state,
name: action.name
};
default:
return state;
}
};
Here is how reduxStore.js looks like:
import {createStore} from 'redux';
import reducer from "./reducers";
const initialState = { name: "initialName " };
export const store = createStore(reducer, initialState);
And at last reduxHelloWorld.js:
import React from 'react';
class HelloWorld extends React.Component{
render(){
return(
<React.Fragment>
<h1>Hello {this.props.name}</h1>
</React.Fragment>
)
}
}
export default HelloWorld;
Now, in the terminal I can see the right values in action.type and action.name . However I think that somehow the part shown below is not working. i.e., I'm unable to update my state variable called name to action.name .
return{
...state,
name: action.name
};
Though I'm getting the right values, my component is not getting rendered with the right values. Any idea what I'm doing wrong over here. Any help would be highly appreciated.
I am really newbie in Redux development. Just started two days ago
Before, I used props - state pattern but I am going to change some parts of state - props pattern to Redux.
First, I will show my codes. index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import ItemList from './itemList';
import SearchList from './searchList';
import {Provider,connect} from 'react-redux';
import {store} from './storeReducer';
import {backToMain} from './reduxAction';
class App extends React.Component{
// not important some codes
render(){
return(
<div>
<section id="Search">
<form action="//localhost:80/ReactStudy/travelReduxApp/public/server/itemList.php" id="Search" className="search" method="GET" onSubmit={this.submitForm}>
<input ref={'search'} placeholder="search country, attraction names" type="text" name="search"/>
<button type="submit">SEARCH</button>
</form>
<p className={this.state.validateError.display}>
{this.state.validateError.type=='invalid value'?
'Only english letters are available in this input'
: (this.state.validateError.type=='not found')?
'Nothing was found with this keyword. Try again'
:'Empty text field'
}
</p>
</section>
<ItemContainer initializeSearch={this.initializeSearch} searchList={this.state.searchItem}/>
</div>
)
}
}
class ItemContainer extends React.Component{
state={
viewMain:true
}
//some codes
showMain=()=>{
this.setState({
viewMain:true
})
this.props.initializeSearch();
store.dispatch(backToMain());
}
render(){
console.log('Item container');
console.log(this.props);
return(
<section id="ItemContainer">
{
this.props.searchList!=''?
<SearchList searchList={this.props.searchList} mainVisible={this.state.viewMain}/>
:
<ItemList toggleView={this.toggleView} mainVisible={this.state.viewMain}/>
}
<button onClick={this.showMain}>Back to main</button>
</section>
)
}
}
const mapStateToProps =(state)=>{
console.log('working'); //it doesn't show it.
return{
visible:state.visible
}
};
const wrappedSearchList = connect(mapStateToProps,null)(ItemContainer);
const Root = () => (
<Provider store={store}>
<App/>
</Provider>
);
ReactDOM.render(
<Root/>,
document.getElementById('App')
)
reduxAction.js
export function backToMain(){
return{
type:'BACK_TO_MAIN'
}
}
storeReducer.js
import {createStore} from 'redux';
import {backToMain} from './reduxAction';
export const initialState = {
visible:true
}
export const store = createStore(viewMainReducer,initialState);
export function viewMainReducer(state=initialState,action){
switch(action.type){
case 'BACK_TO_MAIN':
console.log('Back..');
return{
...state,
visible:true
}
break;
default: return state;
}
}
I am really newbie in Redux so maybe I did not understand official document perfectly but I know mapStateToProps must pass state to the component as props. In my case, the component should be ItemContainer component.
Problem is when ItemContainer component is rendered.
If I check with
console.log(this.props)
in ItemContainer's render method , in console I see only initializeSearch and searchList are passed to the component. I cannot figure out why my visible property of initialState object is not passed to the component. What could be the reason? What should I do to pass visible property to ItemContainer component?
React / Redux: mapStateToProps not actually mapping state to props
read this thread but I just did not understand ...
Try using wrappedSearchList instead of ItemContainer
<wrappedSearchList initializeSearch={this.initializeSearch} searchList={this.state.searchItem}/>**strong text**
I've set up pagination(using a 3rd party component) in one of my React components. Each time a page button is clicked, this function is executed:
handlePageChange= (page)=>{
history.push(`/duplicates?page=${page}`)
}
I'm using "Router" from "react-router-dom", and "createBrowserHistory" from JS history library:
When the component mounts, i simply extract the "page" query parameter, and dispatch a Redux action, that fetches all relevant data, and puts its in the Redux state:
componentDidMount(){
this.props.fetch(this.state.activePage)
}
The "activePage" is taken from the component state:
state={
activePage: this.queryParams.page || 1
}
Everything works nicely, with one very fundamental "flaw": Being that i use history.push on every pagination action, the entire component re-mounts. Sure, i can navigate back and forth in my "pages", and even bookmark them, but the fact that the entire component needs to re-render, seems to undermine one of the main purposes of React: Being very efficient, when it comes to DOM manipulations.
Is there any way to setup pagination, without having to choose between history and efficiency?
EDIT: this is the component:
import React from 'react';
import _ from "lodash";
import { connect } from 'react-redux';
import { searchAction, fetchDuplicates } from '../../actions/products';
import DuplicateProduct from './DuplicateProduct';
import Pagination from 'react-js-pagination'
import queryString from 'query-string';
import {history} from '../../routers/AppRouter';
class Duplicates extends React.Component{
queryParams= queryString.parse(this.props.location.search);
state={
activePage: this.queryParams.page || 1
}
handlePageChange= (page)=>{
history.push(`/duplicates?page=${page}`)
}
componentDidMount(){
this.props.fetch(this.state.activePage)
}
render(){
console.log(this.queryParams.page)
return(
<div>
<h1>Duplicate Titles</h1>
<p>number of pages: {this.props.numberOfPages}</p>
<Pagination
activePage={parseInt(this.state.activePage)}
itemsCountPerPage={150}
totalItemsCount={this.props.numberOfProducts}
pageRangeDisplayed={10}
onChange={this.handlePageChange}
className="pagination"
/>
<br/>
{this.props.duplicates.length>0 &&(
this.props.duplicates[0].map((duplicate_group)=>{
return (
<div key={duplicate_group[0].id}>
<DuplicateProduct duplicate_group={duplicate_group}/>
<hr/>
</div>
)
})
)
}
</div>
);
};
}
const mapStateToProps= (state)=>({
duplicates: state.products.duplicates,
numberOfPages: state.products.numberOfPages,
numberOfProducts: state.products.numberOfProducts
})
const mapDispatchToProps =(dispatch,props)=>({
fetch: (page)=> dispatch(fetchDuplicates(page))
})
export default connect(mapStateToProps, mapDispatchToProps)(Duplicates);
This is the PrivateRoute:
import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
import Header from '../components/Header';
export const PrivateRoute = ({
isAuthenticated,
component: Component,
...rest
}) => (
<Route {...rest} component={(props) => (
isAuthenticated ? (
<div>
<Header />
<Component {...props} />
</div>
) : (
<Redirect to="/" />
)
)} />
);
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isLoggedIn
});
export default connect(mapStateToProps)(PrivateRoute);
I am currently trying to integrate my redux store to my Next.js react applcation. The only issue now is when I try to call connect inside my index.js file.
Maybe it has something to do with the way my app is laid out? I tried console.log(this.props) inside index.js but it doesn't seem to have anything sent down from provider.
Error:
Could not find "store" in either the context or props of "Connect(Page)". Either wrap the root component in a , or explicitly pass "store" as a prop to "Connect(Page)".
page.js
import React from 'react';
import { Provider } from 'react-redux';
import store from '../store/store';
import Head from './head'
import Nav from './nav'
const childPage = (ChildPage) => {
return (
class Page extends React.Component {
render() {
return (
<Provider store={store}>
<div>
<Head />
<Nav />
<ChildPage />
</div>
</Provider>
)
}
}
)
}
export default childPage;
index.js
import React from 'react';
import { connect } from 'react-redux'
import Page from '../components/page';
export class Index extends React.Component {
render() {
return (
<div>
<div className="hero">
</div>
<style jsx>{`
`}</style>
</div>
)
}
}
export default connect(state => state)(Page(Index));
The structure order was incorrect.
export default connect(state=>state)(Page(Index));
This leads to connect() > Provider > Index
export default Page(connect(state=>state)(Index));
This leads to Provider > connect() > Index
So the answer is to do it like this:
export default Page(connect(state=>state)(Index));
You can use next-redux-wrapper npm package. Add withRouter hoc on _app.js page of your app.
Here is example: https://github.com/galishmann/nextjs-redux-example/blob/master/pages/_app.js
I'm creating a application where I have a component which shows data based on the router location
for example
localhost:8080/us should show "USA flag and some text related to United States"
localhost:8080/in should show "India flag and some text related to India"
I have two reducers one for India and other for US and I have a Root reducer as shown bellow
import {combineReducers} from 'redux';
import IndiaData from './IndiaData';
import USData from './UsData';
const rootReducer = combineReducers({
IndiaData,
USData,
ActiveData
});
export default rootReducer;
My Sample reducer IndiaData is as bellow
export default function() {
return [
{
"StateName": "AP",
"StateImg": "../static/image1.jpg",
},
{
"StateName": "TS",
"StateImg": "../static/image2.jpg",
},
{
"StateName": "TN",
"StateImg": "../static/image3.jpg",
}
]
}
I'm using mapStateToProps in my react component, please find the
code below
import React, {Component} from 'react';
import {connect} from 'react-redux';
import styles from '../styles/custom.css'
class CountryData extends React.Component {
Country(){
return this.props.usdata.map((Data)=>{
return(
<div className="col-md-4">
<img className="panel" src={Data.StateImg}/>
<p>{Data.StateName}</p>
</div>
);
})
}
render(){
return(
<div>
<div className="container margin-top jumbotron">
{this.Country()}
</div>
</div>
);
}
}
function mapStateToProps (state){
return {
indiadata: state.IndiaData,
usdata: state.USData,
};
}
export default connect(mapStateToProps)(CountryData);
and my router configuration is as below
import React from 'react';
import {Route, IndexRoute} from 'react-router';
import App from './components/app';
import CountryData from './components/CountryData';
export default (
<Route path ="/" component={App}>
<Route path="in" component={CountryData} />
<Route path="us" component={CountryData} />
</Route>
);
So Finally I should be able to read what is present in the url path and then show US data or India data dynamically, can some one please help me with this?
Thank you for your support in advance!!
You can make a route like so:
And then access that country param in the container via this.props.params.country.
In your mapStateToProps function, you could use this constant to render the correct country data.
return this.props.data.map((Data)=>{
return(
<div className="col-md-4">
<img className="panel" src={Data.StateImg}/>
<p>{Data.StateName}</p>
</div>
);
})
...
// other code here
...
function mapStateToProps (state, props){
return {
data: props.params.country === 'in'
? state.IndiaData
: state.USData
};
}
Another way would be to export two different connected components.
return this.props.data.map((Data)=>{
return(
<div className="col-md-4">
<img className="panel" src={Data.StateImg}/>
<p>{Data.StateName}</p>
</div>
);
})
...
// other code here
...
function IndiaData (state){
return {
data: state.IndiaData
};
}
function USData (state){
return {
data: state.USData
};
}
export const India = connect(IndiaData)(CountryData);
export const US = connect(USData)(CountryData);
then in your routes:
import React from 'react';
import {Route, IndexRoute} from 'react-router';
import App from './components/app';
import { India, US } from './components/CountryData';
export default (
<Route path ="/" component={App}>
<Route path="in" component={India} />
<Route path="us" component={US} />
</Route>
);