I'm trying to map / convert object from props in React. Data is coming from rest-api library and to component via mapStateToProps.
But when i try to use Object.keys its empty
console.log('storagesTree', this.props.storagesTree);
console.log('data', this.props.storagesTree.data);
let items = Object.keys(this.props.storagesTree.data);
console.log('length', items.length);
Below console report screen
UPDATE
this is my raw data from api:
{"#context":"\/admin\/api\/user_storages","#type":"UserStoragesList","user":"5eb2a2ae37f018528939f883","groups":{},"storages":{"first":{"create":true,"read":true,"update":true,"delete":true},"second":{"create":true,"read":true,"update":true,"delete":true}}}
file with fetching data from rest via redux-api library
import "isomorphic-fetch";
import reduxApi, {transformers} from "redux-api";
import adapterFetch from "redux-api/lib/adapters/fetch";
export default reduxApi({
storagesTree: {data: {}},
userInfo: {
url: `/admin/api/user_storages_test`,
postfetch: [
function({data, getState}) {
let {storagesTree} = getState();
for(const storage in data.storages) {
if(data.storages[storage].read === true) {
storagesTree.data[storage] = {};
}
}
}
]
}
}).use("rootUrl", "http://localhost:84").use("fetch", adapterFetch(fetch));
and my component, where i trying to use Object.keys
import React, { Component } from 'react'
import { render } from 'react-dom'
import {connect} from 'react-redux';
class StoragesTree extends Component {
render() {
console.log(JSON.stringify(this.props.storagesTree.data, null, 3))
console.log('storagesTree', this.props.storagesTree);
console.log('data', this.props.storagesTree.data);
var items = Object.keys(this.props.storagesTree.data);
console.log('lengths', items.length);
return (
<div>
<div>StoragesTree</div>
</div>
)
}
}
const mapStateToProps = state => {
const { userInfo, storagesTree } = state
return {
userInfo,
storagesTree,
}
}
export default connect(mapStateToProps)(StoragesTree)
Related
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);
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.
I am new to redux, and I am creating a small app to render an API onClick. I call the action creator inside the componentDidMount which is able to generate the desired outcome the first click. However, because componentDidMount only renders once, nothing happens upon the second "click".
The following code is the component containing the onClick called "handleFavClick" inside this function two action creators are triggered one to identify the button that was clicked and add it to the state for use in the second action creator, which gets the API.
import React from 'react';
import { connect } from 'react-redux';
import '../stylesheet/FavoriteList.css';
import { uploadGif } from '../actions';
import Display from './Display';
class FavoriteList extends React.Component {
handleFavClick = (id) => {
this.props.uploadGif(id);
this.displayGif();
}
displayGif = () => {
this.setState({display: true});
};
renderList = (callback) => {
let fullList = this.props.favsList.map(function(cur, index) {
return (
<button onClick={function() {
callback(index);
}} className="list-item" key={index}>{cur}</button>
)
});
return fullList;
}
render() {
return (
<div>
<div className="favs">
<h2>Favorite List</h2>
<ul className="unordered-list">
{this.renderList(this.handleFavClick)}
</ul>
</div>
{this.state.display && <Display />}
</div>
)
}
}
const mapDispatchToProps = {
uploadGif,
}
const mapStateToProps = (state) => {
return {
favsList: state.myFirstReduxKey,
}
};
export default connect(mapStateToProps, mapDispatchToProps)(FavoriteList);
Display Component (after onClick is triggered, this component is rendered and the display is updated with the data received from the API request)
import React from 'react';
import { connect } from 'react-redux';
import { getGif } from '../actions';
class Display extends React.Component {
componentDidMount() {
const list = this.props.gifList;
const item = this.props.chosenGif;
this.props.getGif(list[item]);
}
.
.
.
.
const mapStateToProps = (state) => {
return {
gifList: state.myFirstReduxKey,
chosenGif: state.uploadGif,
gifs: state.getGif
}
}
export default connect(mapStateToProps, { getGif })(Display);
Called Action Creator
export const getGif = (action) => async dispatch => {
const key = 'GRghbyFwY5CEhc1h7ngS9KBEK9s2W3zBa'
const response = await gifApi.get(`search?q=${action}&api_key=${key}`)
.then(function(response) {
return response.data.data
});
dispatch({ type: GET_GIF, payload: response})
};
At the end of the day, I would like to know how redux programmers handle a client clicking one button, which renders something, then a client clicks a new button which removes the prior click's rendering and renders the new information.
Your code looks fine but there is one thing you need to change to make this work has expected, like how you said since your code is in componentDidMount, this is gonna work only in the Creational life cycle of the component,
now there are two things you can do.
1.**Move your this.props.getGif(list[item]) into **render() (Recomended):
import React from 'react';
import { connect } from 'react-redux';
import { getGif } from '../actions';
class Display extends React.Component {
render(){
const list = this.props.gifList; //this code gets executed every time
const item = this.props.chosenGif;
this.props.getGif(list[item]);
}
}
const mapStateToProps = (state) => {
return {
gifList: state.myFirstReduxKey,
chosenGif: state.uploadGif,
gifs: state.getGif
}
}
export default connect(mapStateToProps, { getGif })(Display);
2.Have a separate handler and have both componentDidMount and componentDidUpdate call it:
import React from 'react';
import { connect } from 'react-redux';
import { getGif } from '../actions';
class Display extends React.Component {
componentDidMount() {
this.loadGifHandler();
}
componentDidUpdate() {
this.loadGifHandler();
}
loadGifHandler = () => {
const list = this.props.gifList;
const item = this.props.chosenGif;
this.props.getGif(list[item]);
}
.
.
.
.
const mapStateToProps = (state) => {
return {
gifList: state.myFirstReduxKey,
chosenGif: state.uploadGif,
gifs: state.getGif
}
}
export default connect(mapStateToProps, { getGif })(Display);
I'm trying to fetch records from backend graphql service and render them with Array.map function. Unfortunately before they're loaded I get error because they are undefined. I tried to set default props on component but it didin't work. Do i have to check if everything is loaded or is there specific way to inject default values into those props. My code looks like that right now
import React from 'react';
import { graphql } from 'react-apollo';
import { fetchTasks } from '../../../graphql/tasks';
import { Dashboard } from '../components/Dashboard';
const propTypes = {
data: React.PropTypes.shape({
tasks: React.PropTypes.array
})
};
const defaultProps = {
data: {
tasks: []
}
};
class DashboardContainer extends React.Component {
render() {
const titles = this.props.data.tasks.map(task => task.title);
return(
<Dashboard
titles={titles}
/>
);
}
}
DashboardContainer.propTypes = propTypes;
DashboardContainer.defaultProps = defaultProps;
export default graphql(fetchTasks)(DashboardContainer);
Yes you have to check if the query has finished to load. You could go through this nice tutorial, where you build a pokemon app. The link points to the part where they show a basic query and how you check if it is loaded.
In your case it could look like this:
import React from 'react';
import { graphql } from 'react-apollo';
import { fetchTasks } from '../../../graphql/tasks';
import { Dashboard } from '../components/Dashboard';
const propTypes = {
data: React.PropTypes.shape({
tasks: React.PropTypes.array
})
};
const defaultProps = {
data: {
tasks: []
}
};
class DashboardContainer extends React.Component {
render() {
if (this.props.data.loading) {
return <div > Loading < /div>;
}
const titles = this.props.data.tasks.map(task => task.title);
return ( <
Dashboard titles = {
titles
}
/>
);
}
}
DashboardContainer.propTypes = propTypes;
DashboardContainer.defaultProps = defaultProps;
export default graphql(fetchTasks)(DashboardContainer);
I am using routes with react-router as below
<Route path="product/:id" component={Product}/>
I am having component product as below code as below
import React, {PropTypes} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import { asyncConnect } from 'redux-async-connect';
import {load, isLoaded} from 'redux/modules/viewlodging';
#asyncConnect([{
promise: ({ store: { dispatch, getState } }) => {
const promises = [];
if (!isLoaded(getState())) {
promises.push(dispatch(load()));
}
return Promise.all(promises);
}
}])
#connect(
state => ({viewdata: state.viewlodging.data}),
dispatch => bindActionCreators({load}, dispatch)
)
export default class Product extends React.Component {
static propTypes = {
viewdata: PropTypes.object,
location: PropTypes.object,
load: PropTypes.func.isRequired
}
render() {
console.log(this.props.routeParams.id); // here I get routeparameter
const { viewdata } = this.props;
return (
<div>
<div>Sample test</div>
</div>
<Footer/>
<Viewfootertext viewdata={viewdata}/>
</div>
);
}
}
I want to pass parameter id to reducer method load, How to pass route parameter here in correct way?
You can send it in either componentWillMount() or componentDidMount(). Don't send it in render method since it fires every time you have new props or state changes.
you can access route params from this.props.params.
So try like this in your container
componentDidMount(){
const {id} = this.props.params;
this.props.load(id); //you can send params values after component get mounted.
}
And your container will look something like this
import React, {PropTypes} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {asyncConnect} from 'redux-async-connect';
import {load, isLoaded} from 'redux/modules/viewlodging';
#asyncConnect([{
promise: ({
store: {
dispatch,
getState
}
}) => {
const promises = [];
if (!isLoaded(getState())) {
promises.push(dispatch(load()));
}
return Promise.all(promises);
}
}])
#connect(
state => ({
viewdata: state.viewlodging.data
}),
dispatch => bindActionCreators({
load
}, dispatch)
)
export default class Product extends React.Component {
static propTypes = {
viewdata: PropTypes.object,
location: PropTypes.object,
load: PropTypes.func.isRequired
}
componentDidMount(){
const {id} = this.props.params;
this.props.load(id); //you can send params values after component get mounted.
}
render() {
console.log(this.props.routeParams.id); // here I get routeparameter
//don't send in render method, since it'll be called many times
const {
viewdata
} = this.props;
return ( < div >
< div > Sample test < /div> < /div> < Footer / >
< Viewfootertext viewdata = {
viewdata
}
/> < /div>
);
}
}
#asyncConnect([{
promise: ({ store: { dispatch, getState }, params: { id }, }) => {
const promises = [];
if (!isLoaded(getState())) {
promises.push(dispatch(load(id)));
}
return Promise.all(promises);
}
}])
Passing id with params worked for me