how can i dispatch store and override initial props in nextjs - reactjs

I want to call API inside getInitialProps then save that response in redux store.so how can I call dispatch store and override initial props
right now after fetching data from API, I'm able to dispatch value to the reducer and it also saving data in store but after saving data my app calling for initial props(i don't know from where it changes the states) and overriding my new saved data into initial props.
main.js
class StepThree extends React.Component {
static async getInitialProps({ reduxStore, req }) {
let encID = req.query.id //null
try {
let encode = await encrption(encID,7,'dec')
const apiCall = await fetch(`${config.leadSearchApi}&search_param=${encode}`);
let res = await apiCall.json();
if(res.data['next_action'] !== "converted"){
let test = await reduxStore.dispatch({ type: PATIENT_NAME,payload:res.data.name });
console.log(test,'res');
await reduxStore.dispatch({ type: PATIENT_NUMBER,payload:res.data['mobile_number'] });
await reduxStore.dispatch({ type: LEAD_ID,payload:res.data.id });
}
} catch (err) {
console.log(err,'get err');
}
return { }
}
render() {
return <div>Hello World </div>
}
}
const mapStateToProps = (state, prevProps) =>{
return{
AmbSelect:state.StepThreeReducer.isAmbSel,
Addons:state.StepThreeReducer.addonSel,
VehcileData:state.StepThreeReducer.vehcileData,
TotalPrice:state.StepThreeReducer.totalPrice,
Cameback:state.StepThreeReducer.comeback,
LeadID:state.Main.LeadId
}
}
export default connect(mapStateToProps,{addOnSelected,priceCal,updateLeadS3,previous,VehicleDataHandler,updateVehData, addonsCount,totalPrice,existLeadData,linkLead2,linkLead3,encrption })(StepThree);
App.js
import App, { Container } from 'next/app'
import React from 'react'
import withReduxStore from '../lib/with-redux-store'
import { Provider } from 'react-redux'
class MyApp extends App {
render () {
const { Component, pageProps, reduxStore } = this.props;
return (
<Container>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</Container>
)
}
}
export default withReduxStore(MyApp)
redux-store.js
import React from 'react'
import { initializeStore } from '../store'
const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'
function getOrCreateStore (initialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {
return initializeStore(initialState)
}
// Create store if unavailable on the client and set it on the window object
if (!window[__NEXT_REDUX_STORE__]) {
window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
}
return window[__NEXT_REDUX_STORE__]
}
export default App => {
return class AppWithRedux extends React.Component {
static async getInitialProps (appContext) {
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrCreateStore()
// Provide the store to getInitialProps of pages
appContext.ctx.reduxStore = reduxStore
let appProps = {}
if (typeof App.getInitialProps === 'function') {
appProps = await App.getInitialProps(appContext)
}
return {
...appProps,
initialReduxState: reduxStore.getState()
}
}
constructor (props) {
super(props)
this.reduxStore = getOrCreateStore(props.initialReduxState)
}
render () {
return <App {...this.props} reduxStore={this.reduxStore} />
}
}
}
store.js
import { createStore, applyMiddleware,combineReducers } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
import './actions';
import reducer from './reducers'
export function initializeStore () {
return createStore(
reducer,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
reducer.js
import {PATIENT_NAME,PATIENT_NUMBER,RIDE_DATE,RIDE_TIME,FCAUSES,SETCAUSE} from '../actions/types';
const INITIAL_STATE = {
patient_name:'',
patient_number:'',
ride_date:false,
ride_time:false,
causes:{},
sel_cause:''
};
export default (state=INITIAL_STATE,action) => {
console.log(action,'reducer')
switch(action.type) {
case PATIENT_NAME:
return {...state,patient_name:action.payload};
case PATIENT_NUMBER:
return {...state,patient_number:action.payload};
case RIDE_DATE:
return {...state,ride_date:action.payload};
case RIDE_TIME:
return {...state,ride_time:action.payload};
case FCAUSES:
return {...state,causes:action.payload};
case SETCAUSE:
return {...state,sel_cause:action.payload};
default:
return state;
}
}
after dispatch I don't want to make app state as initial props
please help,stuckkkkkk

You did't provide an initialState into initializeStore when you creating your store.
To make sure that you're using the same state shape across server and client pass the initialState params to you store initialization inside store.js:
export function initializeStore(initialState = {}) {
return createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
Otherwise same state will always be applied and server-propagated state will be lost.

Related

How to use redux in Next.js?

I am new to Next.js, So I follow some tutorials for Redux integration in Next.js. All is working fine but whenever I switch between pages, each time API make a call, and Redux lost its stored value.
The basic function is like this. Whenever a user loads a website an API call will fetch category data from the server and save that data in reducer[categoryReducer], then the user can navigate to any page and category data will fetched from the reducer. But in my case, it hits again and again
Full Code:
// Action Call
import * as Constants from '../../constant/constant';
import * as t from '../types';
import axios from 'axios';
export const loadCategoryApi = (type) => dispatch => {
axios.post(Constants.getCategories,type)
.then(function (response) {
console.log(response);
if(response && response.data && response.data.status==="200"){
dispatch({
type: t.LOAD_CATEGORY,
value: type
});
}
else if(response && response.data && response.data.status==="404"){
alert('Someting went wrong');
}
})
}
// Reducer File
import * as t from '../types';
const initialState = {
doc:null
}
const CategoryReducer = (state = initialState, action) =>{
console.log('reducer action', action.type);
switch (action.type){
case t.LOAD_CATEGORY:
console.log('slots actions',action);
return({...state, doc:action.value})
default:
return state;
}
}
export default CategoryReducer;
// Store file
import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import { createWrapper } from "next-redux-wrapper"
import rootReducer from "./reducers/rootReducer"
const middleware = [thunk]
const makeStore = () => createStore(rootReducer, compose(applyMiddleware(...middleware)))
export const wrapper = createWrapper(makeStore);
// rootReducer
import { combineReducers } from "redux"
import CategoryReducer from "./CategoryReducer";
const rootReducer = combineReducers({
CategoryReducer: CategoryReducer
})
export default rootReducer;
// _app.js
import React from "react"
import { wrapper } from "../redux/store"
import Layout from '../components/Layout';
import '../styles/globals.css'
const MyApp = ({ Component, pageProps }) =>(
<Layout>
<Component {...pageProps} />
</Layout>
);
export default wrapper.withRedux(MyApp);
// Uses
import React, { useState, useEffect } from 'react';
import {connect} from "react-redux";
import {loadCategoryApi} from "../redux/actions/CategoryAction";
function navbar(props){
const { loadCategory, loadCategoryApi } = props;
useEffect(() => {
if(loadCategory===null){
console.log('navbar loading funciton');
loadCategoryFunation();
}
}, []);
const loadCategoryFunation = () =>{
var json = {
type : 'main'
};
loadCategoryApi(json);
}
}
const mapStateToProps = state => {
return { loadCategory: state.CategoryReducer.doc }
}
const mapDispatchToProps = {
loadCategoryApi
}
export default connect(mapStateToProps, mapDispatchToProps)(Navbar)
What I am doing wrong?
You have to create main reducer to handle the hydration. I explained this hydration process here.
In the file that you created the store, write main reducer
import reducers from "./reducers/reducers";
const reducer = (state, action) => {
// hydration is a process of filling an object with some data
// this is called when server side request happens
if (action.type === HYDRATE) {
const nextState = {
...state,
...action.payload,
};
return nextState;
} else {
// whenever we deal with static rendering or client side rendering, this will be the case
// reducers is the combinedReducers
return reducers(state, action);
}
};
then pass this reducer to the store

React redux action for Twitch extension

I am having some problems with the dispatch of an action under Redux.
I have applied the different tutorials to the letter, but yet this does not take, I always get an empty table in my props.
Here is my code:
Config.js
import React from "react"
import ReactDOM from "react-dom"
import { Provider } from 'react-redux'
import store from './store'
import ConfigPage from "./components/ConfigPage/ConfigPage"
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<ConfigPage />
</Provider>,
rootElement
);
Store
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
import { fetchWalletAddress } from './actions/index'
const store = createStore(rootReducer, applyMiddleware(thunk))
store.dispatch(fetchWalletAddress());
export default store;
Reducer wallet.js
import { GET_WALLET_ADDRESS } from "../actions/actionTypes.js";
const initialState = {
wallet:[]
}
export default function(state = initialState, action){
switch(action.type){
case GET_WALLET_ADDRESS:
return [ ...state, ...action.payload];
default:
return state;
}
}
My action :
import {GET_WALLET_ADDRESS} from './actionTypes.js'
import axios from 'axios'
const apiUrl = 'https://api.elrond.com/accounts/erd15qltd5ccalm5smmgdc5wnx46ssda3p32xhsz4wpp6usldq7hq7xqq5fmn6';
export const fetchWalletAddress = () => {
return (dispatch) => {
return axios.get(apiUrl)
.then(response => {
return response.data
})
.then(data => {
dispatch({
type: GET_WALLET_ADDRESS,
payload: data
})
})
.catch(error => {
throw (error);
});
};
};
And for finish, my Configpage.js
import React from 'react'
import Authentication from '../../util/Authentication/Authentication'
import './Config.css'
import { connect } from 'react-redux'
import { fetchWalletAddress } from '../../actions/index'
class ConfigPage extends React.Component{
constructor(props){
super(props)
this.Authentication = new Authentication()
//if the extension is running on twitch or dev rig, set the shorthand here. otherwise, set to null.
this.twitch = window.Twitch ? window.Twitch.ext : null
this.state={
finishedLoading:false,
theme:'light',
isVisible:true,
wallet_address:'erd15qltd5ccalm5smmgdc5wnx46ssda3p32xhsz4wpp6usldq7hq7xqq5fmn6'
}
this.walletAddressHandler = this.walletAddressHandler.bind(this);
this.onSubmitForm = this.onSubmitForm.bind(this);
}
walletAddressHandler(event){
this.setState({
[event.target.name]:event.target.value
});
}
onSubmitForm(){
fetchWalletAddress();
this.twitch.rig.log(this.props.wallet)
}
contextUpdate(context, delta){
if(delta.includes('theme')){
this.setState(()=>{
return {theme:context.theme}
})
}
}
visibilityChanged(isVisible){
this.setState(()=>{
return {
isVisible
}
})
}
componentDidMount(){
this.twitch.rig.log(this.props.wallet)
if(this.twitch){
this.twitch.onAuthorized((auth)=>{
this.Authentication.setToken(auth.token, auth.userId)
if(!this.state.finishedLoading){
// if the component hasn't finished loading (as in we've not set up after getting a token), let's set it up now.
// now we've done the setup for the component, let's set the state to true to force a rerender with the correct data.
this.setState(()=>{
return {finishedLoading:true}
})
}
})
this.twitch.listen('broadcast',(target,contentType,body)=>{
this.twitch.rig.log(`New PubSub message!\n${target}\n${contentType}\n${body}`)
// now that you've got a listener, do something with the result...
// do something...
})
this.twitch.onVisibilityChanged((isVisible,_c)=>{
this.visibilityChanged(isVisible)
})
this.twitch.onContext((context,delta)=>{
this.contextUpdate(context,delta)
})
}
}
componentWillUnmount(){
if(this.twitch){
this.twitch.unlisten('broadcast', ()=>console.log('successfully unlistened'))
}
}
filterFloat(value) {
if (/^(\-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/
.test(value))
return Number(value);
return NaN;
}
render(){
if(this.state.finishedLoading && this.state.isVisible){
return (
<div className="App">
<div className={this.state.theme === 'light' ? 'App-light' : 'App-dark'} >
<p>Add your wallet address</p>
<input
name="wallet_address"
type="text"
onChange={this.walletAddressHandler}
value={this.state.wallet_address}>
</input>
<p>{this.props.wallet.username}</p>
<button OnClick={this.onSubmitForm}>Try it</button>
<ul>
{this.state.wallet ? String((Number(this.state.wallet.balance) * Math.pow(10, -18)).toFixed(4)) : null}
</ul>
</div>
</div>
)
}else{
return (
<div className="App">
</div>
)
}
}
}
const mapStateToProps = state => {
return {
wallet: state.wallet
}
};
export default connect(mapStateToProps, null)(ConfigPage);
thank you in advance for your help
You do not need to dispatch fetchWalletAddress while creating store, you can do that in the component. Also when you call fetchWalletAddress in the component, make sure to use the function that you make available to component via mapDispatchToProps argument of connect otherwise it wouldn't affect the redux store
Another thing you must do is to not use the updated redux value in the same function call since it takes a render cycle for it to reflect the updated change
store.js
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
const store = createStore(rootReducer, applyMiddleware(thunk))
export default store;
Configpage.js
...
componentDidMount(){
this.props.fetchWalletAddress();
this.twitch.rig.log(this.props.wallet)
if(this.twitch){
this.twitch.onAuthorized((auth)=>{
this.Authentication.setToken(auth.token, auth.userId)
if(!this.state.finishedLoading){
// if the component hasn't finished loading (as in we've not set up after getting a token), let's set it up now.
// now we've done the setup for the component, let's set the state to true to force a rerender with the correct data.
this.setState(()=>{
return {finishedLoading:true}
})
}
})
this.twitch.listen('broadcast',(target,contentType,body)=>{
this.twitch.rig.log(`New PubSub message!\n${target}\n${contentType}\n${body}`)
// now that you've got a listener, do something with the result...
// do something...
})
this.twitch.onVisibilityChanged((isVisible,_c)=>{
this.visibilityChanged(isVisible)
})
this.twitch.onContext((context,delta)=>{
this.contextUpdate(context,delta)
})
}
}
...
componentDidUpdate(prevProps) {
if (!_.isEqual(prevProps.wallet, this.props.wallet)) {
this.twitch.rig.log(this.props.wallet)
}
}
...
onSubmitForm(){
this.props.fetchWalletAddress();// use action from props
}
...
const mapDispatchToProps = {
fetchWalletAddress,
}
const mapStateToProps = state => {
return {
wallet: state.wallet
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ConfigPage);

UI is not re-rendring even though the store state is changing?

I have this main component which is connected to the redux store via connect method.
I am also using logger middleware in order to check the store state as it progressively changes and from there i can see the store is updating successfully but the component it is connected is not re rendering.
Please help....
I have tried almost everything including using Object.assign({}), spread operation and also tried using the componentWillReceiveProps(nextProps) but still the ui is not updating.
Here is the Main app.js file:
import React from 'react'
import { render } from 'react-dom'
import App from './MainComponent'
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import { reactReduxFirebase, getFirebase, firebaseReducer } from 'react-redux-firebase';
import firebase from './fbConfig'
import usersReducer from './reducers/usersReducer'
import logger from 'redux-logger'
// const rootReducer = combineReducers({
// firebase: firebaseReducer,
// });
const data = window.data;
delete window.data;
const store = createStore(usersReducer, data, applyMiddleware(logger(), thunk));
store.subscribe(() => {
// console.log("Store State : " + JSON.stringify(store.getState()));
});
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
)
where data is
{"users":[{"key":1,"value":{"employeeID":1,"firstName":"Siddharth Kilam","mobileNumber":"+919987792049","adminName":"Sid Kilam","adminID":36,"profileName":"default","profileID":4,"explicitLogin":1,"locRow":{"timestamp":"2019-04-09 09:15:05","lat":28.4453983,"lon":77.1012133,"eventTypeID":9,"employeeID":1},"attendanceRow":{"timestamp":"2019-04-09 09:05:39","lat":28.4453983,"lon":77.1012133,"eventTypeID":8,"employeeID":1},"workingStatus":{"code":0,"reason":"Normal Day","shifts":[{"startTime":"2019-04-11T04:34:00.000Z","endTime":"2019-04-11T12:34:00.000Z"}]},"offlinePeriod":3600000,"status":"Inactive"}},{"key":145,"value":{"employeeID":145,"firstName":"SidKilam2 Motorola","mobileNumber":"9599936991","adminName":"Sid Kilam","adminID":36,"profileName":"default","profileID":4,"explicitLogin":1,"locRow":{"timestamp":"2019-04-03 12:20:16","lat":28.4455203,"lon":77.101336,"eventTypeID":9,"employeeID":145},"attendanceRow":{"timestamp":"2019-04-02 23:01:27","lat":28.4747009,"lon":77.0989274,"eventTypeID":9,"employeeID":145},"workingStatus":{"code":0,"reason":"Normal Day","shifts":[{"startTime":"1999-12-31T18:30:00.000Z","endTime":"2000-01-01T18:29:59.000Z"}]},"offlinePeriod":3600000,"status":"Offline"}}]};
Reducer file is
const GET_TASKS = 'get tasks'
export default (state = {}, action) => {
switch (action.type) {
case GET_TASKS:
// return state.usersList.map(emp => {
// return Object.assign({}, emp.value, {
// firstName : "Neeraj Kumar Bansal"
// })
// });
return { ...state, tasks : action.tasks }
default:
return state;
}
}
Action File Is
import database from '../fbConfig'
/**
* ACTION TYPES
*/
const GET_TASKS = 'get tasks'
/**
* ACTION CREATORS
*/
export const getTasks = (tasks) => ({type: GET_TASKS, tasks})
/**
* THUNKS
*/
export function getTasksThunk() {
return dispatch => {
const tasks = [];
database.ref(`/tasks/145/2019-01-14`).once('value', snap => {
// console.log("Called ......................");
snap.forEach(data => {
let task = data.val();
tasks.push(task)
})
// console.log("Tasks Fetched" + tasks);
})
.then(() => dispatch(getTasks(tasks)))
}
}
UI Component IS :
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { firebaseConnect } from 'react-redux-firebase'
import { compose } from 'redux'
import MapView from './components/map/MapView'
import MapComponents from './components/map/MapComponents';
import TasksSidebar from './components/map/TasksSidebar';
import { getTasksThunk } from './thunks/getTasksThunk'
class App extends Component {
render() {
// console.log("Props From Main Component : " + JSON.stringify(this.props.users));
const { users } = this.props;
// const { tasks } = this.state;
console.log("Users From Main Component : " + users);
// console.log("Tasks From Main Component : " + tasks);
return (
<div>
<MapComponents users={users} />
<TasksSidebar />
<MapView users={users}/>
</div>
);
}
}
// export default compose(
// firebaseConnect((props) => {
// return [
// 'Tasks'
// ]
// }),
// connect(
// (state) => ({
// tasks: state.firebase.data.Tasks,
// // profile: state.firebase.profile // load profile
// })
// )
// )(App)
const mapStateToProps = function(state) {
console.log("Map State to props : " + state);
return {
users : state.users,
tasks : state.tasks
}
}
const mapDispatch = dispatch => {
dispatch(getTasksThunk())
return {
}
}
export default connect(mapStateToProps, mapDispatch)(App);
The UI should re render as the store state changes....
Use static getDerivedStateFromProps lifecycle component. As it executes for each re-rendering.
You may check the condition there, if there are no changes just return null otherwise update the state there. In getDerived state from props, you may set the state by returning an object. the setState function won't work here, since it is a static method. kindly refer this link https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
// alter your store and reducer file
const store = createStore(usersReducer, applyMiddleware(logger(), thunk));
const GET_TASKS = 'get tasks';
const initialState = {
users: [{"key":1,"value":{"employeeID":1,"firstName":"Siddharth Kilam","mobileNumber":"+919987792049","adminName":"Sid Kilam","adminID":36,"profileName":"default","profileID":4,"explicitLogin":1,"locRow":{"timestamp":"2019-04-09 09:15:05","lat":28.4453983,"lon":77.1012133,"eventTypeID":9,"employeeID":1},"attendanceRow":{"timestamp":"2019-04-09 09:05:39","lat":28.4453983,"lon":77.1012133,"eventTypeID":8,"employeeID":1},"workingStatus":{"code":0,"reason":"Normal Day","shifts":[{"startTime":"2019-04-11T04:34:00.000Z","endTime":"2019-04-11T12:34:00.000Z"}]},"offlinePeriod":3600000,"status":"Inactive"}},{"key":145,"value":{"employeeID":145,"firstName":"SidKilam2 Motorola","mobileNumber":"9599936991","adminName":"Sid Kilam","adminID":36,"profileName":"default","profileID":4,"explicitLogin":1,"locRow":{"timestamp":"2019-04-03 12:20:16","lat":28.4455203,"lon":77.101336,"eventTypeID":9,"employeeID":145},"attendanceRow":{"timestamp":"2019-04-02 23:01:27","lat":28.4747009,"lon":77.0989274,"eventTypeID":9,"employeeID":145},"workingStatus":{"code":0,"reason":"Normal Day","shifts":[{"startTime":"1999-12-31T18:30:00.000Z","endTime":"2000-01-01T18:29:59.000Z"}]},"offlinePeriod":3600000,"status":"Offline"}}],
tasks: []
}
export default (state = initialState, action) => {
switch (action.type) {
case GET_TASKS:
return { ...state, tasks : action.tasks }
default:
return state;
}
}
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { firebaseConnect } from 'react-redux-firebase'
import { compose } from 'redux'
import MapView from './components/map/MapView'
import MapComponents from './components/map/MapComponents';
import TasksSidebar from './components/map/TasksSidebar';
import { getTasksThunk } from './thunks/getTasksThunk'
class App extends Component {
constructor(){
super();
this.state = {
users: []
}
}
static getDerivedStateFromProps(props, state){
if(props.users !== state.users){
return {
users: props.users // This will update the props value for users in state
}
}
return null;
}
render() {
// console.log("Props From Main Component : " + JSON.stringify(this.props.users));
const { users } = this.state;
// const { tasks } = this.state;
console.log("Users From Main Component : " + users);
// console.log("Tasks From Main Component : " + tasks);
return (
<div>
<MapComponents users={users} />
<TasksSidebar />
<MapView users={users}/>
</div>
);
}
}
const mapStateToProps = function(state) {
//console.log("Map State to props : " + state);
return {
users : state.users,
tasks : state.tasks
}
}
const mapDispatch = dispatch => {
dispatch(getTasksThunk())
return {
}
}
export default connect(mapStateToProps, mapDispatch)(App);

Could not find store in either the context or props of connect site redux error

I am trying to create something similar to todo app with react and redux from here.I have been reading all the solutions for this problem and nothing seems to be working for my case.
Most of the solutions purpose using Provider which I am already using. Any help is much appreciated.
Edit - Few import statement might be missing in snippets, but all components are being imported whenever needed and actions are also defined in action file.
Index.js
import App from './components/App'
import reducer from './reducers/index'
const store = createStore(reducer)
const AppWithStore = (
<Provider store={store}>
<App />
</Provider>
)
ReactDOM.render(AppWithStore, document.getElementById('root'))
Update - Combined Reducer code
import React from 'react'
import { combineReducers } from 'redux'
import TestReducer from './TestReducer'
export default combineReducers({
TestReducer,
})
App.js
import Test from './Test';
class App extends Component {
render() {
return (
<Test />
);}
}
ReactDOM.render(
(<App/>),
document.getElementById("root")
);
export default App
Test.js contains both component and container
import { connect } from 'react-redux'
import { add } from '../actions'
const mapStateToProps = state => ({
todos: state.todos,
})
class Test extends Component {
dosomething() {
const dispatch = this.props;
dispatch(add("New Note"));
}
render() {
return (
<div>
< button OnClick = { this.dosomething.bind(this)} > Test </button>
</div>
)
}
}
export default connect(mapStateToProps)(Test)
The reducer logic for Test is as given below
import React from 'react';
const TestReducer = (state = [], action) => {
const todos = state;
const {type, payload} = action;
switch(action.type){
case 'ADD': {
return {
...state,
todos:"new todo"
}
}
}
return state;
}
export default TestReducer
You should remove
ReactDOM.render(
(<App/>),
document.getElementById("root")
); from App.js file
When you call it again in App.js a new component instance independent of others is created, That's why it is not finding store.As store is not passed to it.
You can check it here https://codesandbox.io/s/vy7wwqw570. As i had remove render api call from app.js it is working now.
you need to import your reducers so that you
if your file name is TestReducer.js
import React from 'react';
export const TestReducer = (state = [], action) => {
const todos = state;
const {type, payload} = action;
switch(action.type){
case 'ADD': {
return {
...state,
todos:"new todo"
}
}
}
return state;
}
then import it in this manner
import {TestReducer} from './TestReducer.js';
const store = createStore(TestReducer);
Try replacing todos: state.todos with todos: state.TestReducer.todos
import { connect } from 'react-redux'
import { add } from '../actions'
const mapStateToProps = state => ({
//todos: state.todos,
todos: state.TestReducer.todos
})
class Test extends Component {
dosomething() {
const dispatch = this.props;
dispatch(add("New Note"));
}
render() {
return (
<div>
< button OnClick = { this.dosomething.bind(this)} > Test </button>
</div>
)
}
}
export default connect(mapStateToProps)(Test)

React Redux how to dispatch async actions and update state

I try to deal with ajax data in my learning react,redux project and I have no idea how to dispatch an action and set the state inside a component
here is my component
import React, {PropTypes, Component} from 'react';
import Upload from './Upload';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as profileActions from '../../../actions/profileActions';
class Profile extends Component {
static propTypes = {
//getProfile: PropTypes.func.isRequired,
//profile: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
profile:{
username: '',
password: ''
}
}
this.onUpdate = this.onUpdate.bind(this)
}
onUpdate(event) {
alert()
}
componentWillMount() {
}
componentDidMount() {
}
render() {
const {profile} = this.props;
return (
<div>
</div>
);
}
}
function mapStateToProps(state) {
console.log(state)
return {
profile: state.default.profile,
};
}
function mapDispatchToProps(dispatch, ownProps) {
return {
actions: bindActionCreators(profileActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
I create the store as it follows
import { createStore, combineReducers, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import thunk from 'redux-thunk'
import { routerReducer, routerMiddleware, push } from 'react-router-redux'
import reducers from '../reducers'
import { browserHistory } from 'react-router';
const middleware = [ thunk ];
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger());
}
middleware.push(routerMiddleware(browserHistory));
// Add the reducer to your store on the `routing` key
const store = createStore(
combineReducers({
reducers,
routing: routerReducer
}),
applyMiddleware(...middleware),
)
export default store;
reducer
export const RESOLVED_GET_PROFILE = 'RESOLVED_GET_PROFILE'
const profileReducer = (state = {}, action) => {
switch (action.type) {
case 'RESOLVED_GET_PROFILE':
return action.data;
default:
return state;
}
};
export default profileReducer;
actions
import * as types from './actionTypes';
import Api from '../middleware/Api';
export function getProfile() {
return dispatch => {
dispatch(setLoadingProfileState()); // Show a loading spinner
Api.getAll('profile').then(profile => {
dispatch(doneFetchingProfile(profile));
}).catch(error => {
throw(error);
});
/*Api.fetch(`profile`, (response) => {
console.log(response)
dispatch(doneFetchingBook()); // Hide loading spinner
if(response.status == 200){
dispatch(setProfile(response.json)); // Use a normal function to set the received state
}else {
dispatch(error)
}
}) */
}
}
function setProfile(data) {
return {type: types.SET_PROFILE, data: data}
//return { type: types.SET_PROFILE, data: data };
}
function setLoadingProfileState() {
return {type: types.SHOW_SPINNER}
}
function doneFetchingProfile(data) {
console.log(data)
return {
type: types.HIDE_SPINNER,
profile: data
}
}
function error() {
return {type: types.SHOW_ERROR}
}
but I have no idea how would I dispatch action and update the state after getProfile action
You need to only dispatch your event RESOLVED_GET_PROFILE right after dispatching doneFetchingProfile, or simply listen RESOLVED_GET_PROFILE and hide spinner on reducing it.
Api.getAll('profile').then(profile => {
dispatch(doneFetchingProfile(profile));
dispatch(resoloveGetProfile(profile));
})
Actually you r doing everything right - so I didn't understand what is your question is, so if you meant something else - let me know, I`ll try to describe you.
About dispatch(resoloveGetProfile(profile));
There you dispatch action, which will update your state, simple as you do with some static state, I saw that you already have setProfile action, so you can change that line, to call your existed function.
dispatch(setProfile(profile))
Than you need to reduce your state in this action
case 'SET_PROFILE' : (action, state) => {...state, profile: action.data}
Than your state will change and your components will update. Note that your 'get profile method' better to call from componentDidMount to avoid freezing at rendering because of performing web request.

Resources