Error undefined is not a function when change text - react-redux

I am trying the increment and decrement tutorial . When I type a number at the textinput, this value is to be reflected to the this text given. But I am getting error. The error says
undefined is not a function(evaluating 'this.props.counterSet(count)')
These are the codes that I have tried. Can anybody tell me where I have done the mistake.
Thank You
- App
-Actions
-ActionTypes.js
-CounterAction.js
-Reducers
-counterReducer.js
app.js
counterReducer.js
export default (state = 0, action) => {
switch (action.type) {
case 'SET':
return action.payload;
default:
return state;
}
}
counterAction.js
export const counterSet = (receivedNumber) => {
return {
type: 'SET',
payload: receivedNumber
}
}
ActionTypes.js
export * from './CounterAction';
app.js
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, TextInput, View, Button } from 'react-native';
import { connect } from 'react-redux';
import { counterSet } from './Actions/ActionTypes';
class App extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
this.onChangeText = this.onChangeText.bind(this);
}
onChangeText(number) {
let count = parseInt(number);
// alert("inside", count);
this.props.counterSet(count);
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{ width: 40, height: 40, borderWidth: 1 }}
onChangeText={this.onChangeText}
value={this.props.count.toString()}
/>
<View style={styles.countViewStyle}>
<Text style={styles.welcome}>
{this.props.count}
</Text>
</View>
</View>
);
}
}
function mapStateToProps(state) {
return {
count: state
}
}
export default connect(mapStateToProps, { counterIncrement, counterDecrement, counterClear, counterSet })(App);

Change the import from
export * from './CounterAction';
to
export { counterSet } from './CounterAction;
Hope this will help!

Related

How do I render an image using map function?

I've tried do render the image from URL but I have no success. If write the URI ok, but if I write item.show.image.original.replace ('http:', 'https:') to get the image no success.
The problem is there´s no error but not rendering.
The others values I've had success with.
import React, { Component } from "react";
import { Text, View, Image } from "react-native";
let termo = 'batman';
const API = 'http://api.tvmaze.com/search/shows?q='+termo;
export default class mapFunction extends Component {
constructor(props) {
super(props);
this.state = {
array: [], };
}
componentDidMount() {
fetch(API)
.then(response => response.json())
.then(data => this.setState({ array: data}));
}
list = () => {
return this.state.array.map(item => {
return (
<View style={{marginLeft: 10}}>
<Text>{item.score}</Text>
<Text>{item.show.name}</Text>
<Text>{item.show.type}</Text>
<Text>{item.show.language}</Text>
<Text>{item.show.summary} </Text>
<Image source={{uri:'https://static.tvmaze.com/uploads/images/original_untouched/6/16463.jpg'}}
style={{width:90, height:150}} />
</View>
);
});
};
render() {
return <View>{this.list()}
</View>;
}
}
Just change render function as below:
render() {
return (
<View>{this.list()}</View>
);
}

Redux property increment not working, if call through child component

I have flatlist having images, videos. for images, I defined duration to show and then move to the next item in flatlist, but in case of the video once a video ended then move to next item.
I am using Redux for currentlyPlayingIndex and flatlist datasource.
If I have only images in flatlist it is working fine, but if I have a video, on video end I need to pass event from child to its parent. The parent calls the same method to move to the next index as for image duration end but the increment of currentlyPlayingIndex by one is not happening in case of video end.
parent component code or flatlist handler
import React, { Component } from 'react'
import { TouchableWithoutFeedback, View,StatusBar,Dimensions,FlatList } from 'react-native'
import {FileType,getFileType,getFileExtension} from '../services/FileManagerService'
import VideoPlayer from '../components/PlayerTypes/VideoPlayer'
import ImagePlayer from '../components/PlayerTypes/ImagePlayer'
import PdfPlayer from '../components/PlayerTypes/PdfPlayer'
import { ScaledSheet } from 'react-native-size-matters';
import NothingTpPlay from '../components/PlayerTypes/NothingTpPlay'
//redux
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../database/actions/ScheduleActions.js'; //Import your actions
import * as Animatable from 'react-native-animatable';
import constants from '../config/constants'
import KeepAwake from 'react-native-keep-awake';
import AudioPlayer from '../components/PlayerTypes/AudioPlayer'
import { showToastMessage } from '../utils/ToastMessage'
import I18n from "../locales/i18n-js";
import WebsitePlayer from '../components/PlayerTypes/WebsitePlayer'
import FullScreen from "../NativeBridgingHeader/FullScreen";
let deviceWidth = Dimensions.get('window').width
let deviceHeight = Dimensions.get('window').height
class PlaylistPlayerScreen extends Component {
constructor() {
super();
this.state = {
currentVisibleIndex:-1
}
this.playNextFile = this.playNextFile.bind(this)
this.videoEnded = this.videoEnded.bind(this)
this.schedulePlayDurationTimer = this.schedulePlayDurationTimer.bind(this)
this.viewabilityConfig = {
waitForInteraction: false,
itemVisiblePercentThreshold: 99,
}
}
static navigationOptions = {
header: null,
};
onViewableItemsChanged = ({ viewableItems }) => {
// viewableItems will show you what items are in view
// console.log("onViewableItemsChanged called" + JSON.stringify(viewableItems))
if(viewableItems.length >= 1) {
const visibleFileIndex = viewableItems[0].index
// console.log("visible index " + visibleFileIndex)
this.setState({currentVisibleIndex:visibleFileIndex})
const file = this.props.schedulesFiles[visibleFileIndex]
const fileType = getFileType(file)
console.log("file type is " + fileType)
if (fileType == FileType.Video) {
console.log("video file type")
} else {
this.schedulePlayDurationTimer(visibleFileIndex)
}
}
}
getItemLayout = (data, index) => ({
length: deviceWidth,
offset: deviceWidth * index,
index,
})
componentDidMount(){
this.props.getScheduleFiles()
}
shouldComponentUpdate(nextProps, nextState) {
return true
}
componentDidUpdate(){
console.log("componentDidUpdate")
}
schedulePlayDurationTimer(file_index) {
const file = this.props.schedulesFiles[file_index]
const playDuration = file.play_duration_in_milliseconds
this.timer = setTimeout(() => {
clearTimeout(this.timer)
this.playNextFile()
}, playDuration);
}
videoEnded = () => {
console.log("video ended")
this.playNextFile()
}
playNextFile = () => {
if(this.props.currentlyPlayingIndex == (this.props.schedulesFiles.length - 1)) {
//last file played
this.props.getScheduleFiles()
this.props.playNextFile(this.props.schedulesFiles,this.props.currentlyPlayingIndex)
this.listRef.scrollToIndex({animated: false, index: this.props.currentlyPlayingIndex})
} else {
console.log("playNextFile current index " + this.props.currentlyPlayingIndex)
this.props.playNextFile(this.props.schedulesFiles,this.props.currentlyPlayingIndex)
console.log("playNextFile next index " + this.props.currentlyPlayingIndex)
this.listRef.scrollToIndex({animated: true, index: this.props.currentlyPlayingIndex})
}
}
_renderItem = ({item, index}) => {
return (
this.renderPlayer(item,index)
);
}
renderPlayer(file,index) {
switch (getFileType(file)) {
case FileType.Video:
return <VideoPlayer file={file} onEnd={this.videoEnded} currentIndex={index} currentVisibleIndex={this.state.currentVisibleIndex} />
case FileType.Audio:
return <AudioPlayer file={file} onEnd={this.playNextFile} />
case FileType.Image:
return <ImagePlayer file={file} onEnd={this.playNextFile} />
case FileType.Pdf:
return <PdfPlayer file={file} onEnd={this.playNextFile} />
case FileType.WebpageContent:
return <WebsitePlayer file={file} onEnd={this.playNextFile} />
default:
showToastMessage(
I18n.t('ErrorMessage.FormatNotSupported', {
name: getFileExtension(file).toUpperCase()
})
)
this.playNextFile()
}
}
render() {
if(this.props.schedulesFiles.length > 0 ) {
return (
<View style={{flex:1}}>
<StatusBar hidden={true} />
<FlatList
style={{flex:1}}
bounces={false}
removeClippedSubviews={true}
scrollEnabled={false}
showsHorizontalScrollIndicator={false}
ref={el => this.listRef = el}
horizontal={true}
keyExtractor={(item, index) => index.toString()}
data={this.props.schedulesFiles}
renderItem={this._renderItem}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
getItemLayout={this.getItemLayout}
initialNumToRender={2}
maxToRenderPerBatch={2}
windowSize={this.props.schedulesFiles.length}
/>
<KeepAwake />
</View>
)
}else {
return (
<TouchableWithoutFeedback delayLongPress={constants.REVEAL_SIDE_BAR_MENU_PRESS_DURATION} onLongPress={() => this.props.navigation.openDrawer()}>
<View style={styles.container}>
<NothingTpPlay/>
<KeepAwake />
</View>
</TouchableWithoutFeedback>
)
}
}
}
const styles = ScaledSheet.create({
container: {
flex:1,
backgroundColor : 'white',
}
});
//redux binding
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.scheduleReducer.loading,
schedulesFiles: state.scheduleReducer.data,
currentlyPlayingIndex: state.scheduleReducer.nextFileIndex,
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(PlaylistPlayerScreen);
Render method for video player child component code is:
<VideoPlayer file={file} onEnd={this.videoEnded} currentIndex={index} currentVisibleIndex={this.state.currentVisibleIndex} />
VideoPlayer.js relevant code
export default class VideoPlayer extends React.Component {
constructor() {
super();
this.state = {
}
this.videoEnded = this.videoEnded.bind(this)
}
videoEnded() {
if (this.props.shouldRepeat == true) {
} else {
this.video.paused = true
this.video.seek(0)
}
this.props.onEnd()
}
render() {
return (
<Video
ref={ref => {
this.video = ref;
}}
onError={this.videoEnded}
minLoadRetryCount={1}
useTextureView={true}
controls={false}
style={ContainerStyle.playerTypeStyle}
onEnd={this.videoEnded}
repeat={this.props.shouldRepeat}
playInBackground={false}
playWhenInactive={false}
ignoreSilentSwitch={"ignore"}
resizeMode={this.props.file.resize_mode}
source={{uri:getFileAbsolutePath(this.props.file)}}
paused={this.props.currentIndex != this.props.currentVisibleIndex}
/>
)
}
}
Reducer code
import { combineReducers } from 'redux';
import { SCHEDULE_REFRESHED,PLAY_NEXT_FILE } from "../actions/ScheduleActions.js" //Import the actions types constant we defined in our actions
let dataState = { data: [], loading:true };
const scheduleReducer = (state = dataState, action) => {
switch (action.type) {
case SCHEDULE_REFRESHED:
state = Object.assign({}, state, { data: action.data, nextFileIndex:action.nextFileIndex });
return state;
case PLAY_NEXT_FILE:
state = Object.assign({}, state, { nextFileIndex: action.nextFileIndex});
return state;
default:
return state;
}
}
// Combine all the reducers
const rootReducer = combineReducers({
scheduleReducer
// ,[ANOTHER REDUCER], [ANOTHER REDUCER] ....
})
export default rootReducer;
Action code
//gets called initially on app launch to get files to be played
export function getScheduleFiles(){
return (dispatch) => {
getOfflineNextScheduleFiles().then((files)=>{//get offline files/schedule first
plainFiles = convertToArray(files)
dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
}).catch((error)=>{//if offline schedules is not available to play, refresh online
triggerPlaylistsRefresh().then((files)=>{
plainFiles = convertToArray(files)
dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
}).catch((error)=>{
console.log("nothing to play")
dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
showToastMessage(I18n.t("ErrorMessage.NoSchedulesAvailableForCurrentTimeError"))
})
})
}
}
//get called from PlaylistPlayerScreen after each file played
export function playNextFile(files,filePlayedIndex){
return (dispatch) => {
if(filePlayedIndex < files.length-1) {
dispatch({type: PLAY_NEXT_FILE, nextFileIndex:filePlayedIndex+1});
}else {
console.log("all files played")
dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
}
}
}

How to show Timeout for 25000 ms if API does not give data using redux?

I am new to react and redux. I have implemented API fetching using redux but not sure where should i put code for Timeout if API does not give gives data for particular time. everything is working fine I am getting data too..only thing i stuck is how to show timeout. Is there any way to do that? Thanks in advance :)
reducer.js
export const GET_REPOS = 'my-awesome-app/repos/LOAD';
export const GET_REPOS_SUCCESS = 'my-awesome-app/repos/LOAD_SUCCESS';
export const GET_REPOS_FAIL = 'my-awesome-app/repos/LOAD_FAIL';
const initialState = {
repos: [],
loading: false,
error: null
};
export default function reducer(state = initialState , action) {
switch (action.type) {
case GET_REPOS:
return { ...state, loading: true };
case GET_REPOS_SUCCESS:
return { ...state, loading: false, repos: action.payload.data };
case GET_REPOS_FAIL:
return {
...state,
loading: false,
error: 'Error while fetching repositories',
};
default:
return state;
}
}
export function listRepos(photos) {
return {
type: GET_REPOS,
payload: {
request: {
url: `photos/`
}
}
};
}
export function listThumb(albumId) {
return {
type: GET_REPOS,
payload: {
request: {
url: `photos?albumId=${albumId}`
}
}
};
}
home.js
import React, { Component } from 'react';
import { ActivityIndicator } from 'react-native-paper';
import { View, Text, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import styles from '../HomeComponent/style';
import { Ionicons } from '#expo/vector-icons';
import { listRepos } from '../../../reducer';
import ErrorAlert from '../../common/ErrorAlertComponent/errorAlert';
class Home extends Component {
componentDidMount() {
this.props.listRepos('');
}
FlatListItemSeparator = () => (
<View style={styles.flatListItemSeparator} />
)
renderItem = ({ item }) => (
<View style={styles.listRowContainer}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('ThumbnailViewScreen', {
albumID: item.id,
})} style={styles.listRow}>
<View style={styles.listTextNavVIew}>
<Text style={styles.albumTitle}> {item.title} </Text>
<Ionicons name='md-arrow-dropright' style={styles.detailArrow} />
</View>
</TouchableOpacity>
</View>
);
render() {
const { error, loading, products } = this.props;
if (error) {
return <ErrorAlert />;
}
if (loading) {
return (
<View style={{ flex: 1, paddingTop: 30 }}>
<ActivityIndicator animating={true} size='large' />
</View>
);
}
const { repos } = this.props;
return (
<View style={styles.MainContainer} >
<FlatList
styles={styles.container}
data={repos}
renderItem={this.renderItem}
ItemSeparatorComponent={this.FlatListItemSeparator}
/>
</View>
);
}
}
const mapStateToProps = state => {
let storedRepositories = state.repos.map(repo => ({ key: repo.id.toString(), ...repo }));
return {
repos: storedRepositories,
loading: state.loading,
error: state.error
};
};
const mapDispatchToProps = {
listRepos
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Firstly you should create entry point for your axios. For example:
import axios from 'axios/index';
const api = axios.create({
baseURL: 'site.com',
timeout: 25000,
});
export default api;
And import it where you do api calls:
import api from 'yourDirectory';
And use this entry point:
api.get(url)
If request time is too long axios throws timeout error after 25000ms

react native navigation - componentDidMount() fired twice

I am new to React Native. I am trying to build an app which has a Splash screen that would later navigate to Login screen if a user has not been authenticated or the Main screen if the user is authenticated. This is done using this.props.navigation.navigate()
The problem is that the Splash component would be mounted twice. I checked this by printing inside componentDidMount() of Splash. Because of this, the Login/Main screen enters twice, which looks very unpleasant. Is there any way to fix this?
Also, I want to add some delay when the screen changes from Splash to Login or Main using setTimeout(). Anyway to go about doing this?
Here's my code:
index.js
import React from 'react';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
import reduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import { StyleSheet } from 'react-native';
import LoginScreen from './src/components/Login/LoginScreen';
import Splash from './src/components/Login/Splash';
import Navigation from './src/components/Navigation/Navigation';
import { Font } from 'expo';
import {
createStackNavigator
} from 'react-navigation';
const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = createStoreWithMiddleware(reducers);
const persistor = persistStore(store);
export default class App extends React.Component {
constructor(props){
super(props);
this.state = {
fontLoaded: false,
currentScreen: 'Splash',
};
setTimeout(() => this.setState({currentScreen: 'Login'}), 2000);
}
async componentDidMount() {
await Font.loadAsync({
'Quicksand': require('./assets/fonts/Quicksand-Regular.ttf'),
'Quicksand-Medium': require('./assets/fonts/Quicksand-Medium.ttf'),
'Quicksand-Bold': require('./assets/fonts/Quicksand-Bold.ttf'),
});
this.setState({ fontLoaded: true });
}
render() {
const MainNavigator = createStackNavigator({
Splash: { screen: Splash },
Main: { screen: Navigation },
Login: { screen: LoginScreen },
})
if (this.state.fontLoaded)
return (
<Provider store={store}>
<MainNavigator></MainNavigator>
</Provider>
)
else return null;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Splash.js
import React from 'react';
import { StyleSheet, Text, View, ImageBackground, Image, Button } from 'react-native';
import bgImage from '../../../assets/images/login-background2.png';
import logo from '../../../assets/images/app-logo.png';
import { connect } from 'react-redux';
import { checkAuth } from '../../actions/auth.actions';
class Splash extends React.Component {
static navigationOptions ={
header: null
}
constructor(props){
super(props);
this.state = {
stillLoading: true,
}
}
componentDidMount() {
this.props.checkAuth();
}
render() {
if (this.props.authState.isLoginPending)
return (
<ImageBackground source={bgImage} style={styles.backgroundContainer}>
<View style={styles.logoContainer}>
<Image source={logo} style={styles.logo}></Image>
<Text style={styles.logoText}> Welcome to HealthScout</Text>
</View>
</ImageBackground>
);
else if (this.props.authState.isLoginSuccess){
setTimeout(() => this.props.navigation.navigate('Main'));
return null;
}
else{
setTimeout(() => this.props.navigation.navigate('Login'));
return null;
}
}
}
const mapStateToProps = state => {
return {
authState: state.authState
}
}
const mapDispatchToProps = dispatch => {
return {
checkAuth: () => dispatch(checkAuth()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Splash);
const styles = StyleSheet.create({
backgroundContainer: {
flex: 1,
alignItems: 'center',
width: null,
height: null,
justifyContent: 'center',
},
logoContainer: {
alignItems: 'center',
},
logo: {
width: 110,
height: 149,
},
logoText: {
color: '#fff',
fontSize: 40,
fontFamily: 'Quicksand',
opacity: 0.7,
marginTop: 20,
marginBottom: 10,
textAlign: 'center',
},
});
Solution
Take out the createStackNavigator from render.
It is better way wrapping screens above App class.
const MainNavigator = createStackNavigator({
Splash: { screen: Splash },
Main: { screen: Navigation },
Login: { screen: LoginScreen },
})
export default class App extends React.Component {
...
Why?
render is run repeatedly depends on various conditions as changing state, props and so on.
And your code looks making multiple components with createStackNavigation in render. Take out :)
p.s If you want to wait loading fonts before show home screen, just change to home screen from splash screen after loaded fonts. Thus, the better way is loading fonts in SplashScreen and do what you want.

Is there a simple way of implementing a column picker for a List?

We are going to implement a columnpicker and currently the only idea I have is to implement a ColumnPickableList that wraps a List. This would also hold a list of checkboxes that will enable the user to hide a column.
But before I go ahead do that I just wondered if I'm reinveting the wheel and if there is a simpler approach to solving this?
No simpler way. You'll have to implement your own List component for that
I'm following up on this since I'm struggling to make this work. Maybe it is because I have chosen to create a wrapper that filters the children to be displayed. So techically this approach doesn't implement its own List.
I have made a naive draft which I was hoping would work, but it fails to re-render the children even though they are changed/filtered in the parent component.
The console.log(..) in ColumnPickableList render()-function does print the correct children/props, but still the children won't update/re-render. Any clues as to why? Is this approach too naive?
So here is the current draft:
ColumnPicker.js
import React, { PropTypes } from 'react';
import Checkbox from 'material-ui/Checkbox';
export default class ColumnPicker extends React.Component {
constructor(props) {
super(props);
this.onCheck = this.onCheck.bind(this);
}
onCheck(column, isChecked) {
return this.props.onCheckboxChanged(column, isChecked);
}
renderCheckbox(column, onCheck) {
const disabled = (column.source === 'id');
return (<Checkbox key={column.source} label={column.source.toUpperCase()} onCheck={(event, checked) => onCheck(column, checked)} defaultChecked disabled={disabled} />);
}
render() {
const columns = this.props.columns || [];
return (
<div className="column-picker">
{columns.map((column) => {
return this.renderCheckbox(column, this.onCheck);
})}
</div>
);
}
}
ColumnPicker.propTypes = {
columns: PropTypes.array,
onCheckboxChanged: PropTypes.func,
};
ColumnPicker.defaultProps = {
columns: [], // [{source: myField, checked: true} ...]
};
ColumnPickableList.js:
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { List, Datagrid } from 'admin-on-rest';
import ColumnPicker from './ColumnPicker';
import { toggleColumnPickerStatusAction, initializeColumnPickerAction } from './actions';
export class ColumnPickableList extends React.Component {
componentWillMount() {
let columnSourceNames = [];
if (this.props.children) {
columnSourceNames = React.Children.map(this.props.children, (child) => {
return ({ source: child.props.source, checked: true });
});
}
const columnsDisplayed = columnSourceNames.filter((column) => column.source);
this.props.initializeColumnPicker(this.props.resource, columnsDisplayed);
}
shouldComponentUpdate(nextProps) {
const diff = nextProps.columnsDisplayed.filter((currentColumn) => {
return !this.props.columnsDisplayed.some((prevColumn) => {
return currentColumn.source === prevColumn.source && currentColumn.checked === prevColumn.checked;
});
});
return diff.length > 0;
}
removeHiddenColumns(children) {
return React.Children.map(children, (child) => {
if (!child.props.source) {
return child;
}
const column = this.props.columnsDisplayed.find((columnDisplayed) => {
return columnDisplayed.source === child.props.source;
});
if (this.props.columnsDisplayed.length === 0 || (column && column.checked)) {
return React.cloneElement(child);
}
return null;
});
}
render() {
const { children, ...rest } = this.props;
const displayedChildren = this.removeHiddenColumns(children);
console.log('Does it render? Rendering children', displayedChildren.map((child) => child.props.source));
return (
<div className="columnpickable-list">
<ColumnPicker columns={this.props.columnsDisplayed} onCheckboxChanged={this.props.handleCheckboxChanged} />
<List {...rest}>
<Datagrid>
{displayedChildren}
</Datagrid>
</List>
</div>
);
}
}
ColumnPickableList.propTypes = {
resource: PropTypes.string,
columnsDisplayed: PropTypes.array,
children: PropTypes.node,
initializeColumnPicker: PropTypes.func,
handleCheckboxChanged: PropTypes.func,
};
ColumnPickableList.defaultProps = {
columnsDisplayed: [],
};
function mapStateToProps(state) {
return {
columnsDisplayed: state.columnsDisplayed || [],
};
}
actions.js:
export const actions = {
INIT_COLUMNPICKER: 'INIT_COLUMNPICKER',
TOGGLE_COLUMNPICKER_STATUS: 'UPDATE_COLUMNPICKER_STATUS',
UPDATE_COLUMNPICKER_STATUSES: 'UPDATE_COLUMNPICKER_STATUSES',
}
export function initializeColumnPickerAction(resource, columns) {
return {
type: actions.INIT_COLUMNPICKER,
columns,
meta: { resource },
};
}
export function toggleColumnPickerStatusAction(column) {
return {
type: actions.TOGGLE_COLUMNPICKER_STATUS,
column,
};
}
reducers.js:
import { actions } from './actions';
function columnPickerReducer(state = [], action) {
switch (action.type) {
case actions.INIT_COLUMNPICKER: {
console.log('Init columnopicker reducer');
return action.columns;
}
case actions.TOGGLE_COLUMNPICKER_STATUS: {
const columns = state.map((column) => {
if (column.source === action.column.source) {
return { ...column, checked: !column.checked };
}
return column;
});
return columns;
}
default:
return state;
}
}
export default columnPickerReducer;
Example snippet of parent component:
...
<ColumnPickableList title="SillyStuff" {...props}>
<TextField source="id" />
<TextField source="NAME" />
<TextField source="SILLY_NAME" />
<TextField source="CHANGED_BY" />
<DateField source="CHANGED_TS" showTime />
<EditButton />
<DeleteButton />
</ColumnPickableList>
...

Resources