All:
I am pretty new to redux and react-redux, I wonder if I design a todo item component(the element unit of a todolist) like
const todo => ({id, todocontent}) {
return <li key={id}>{todocontent}</li>
}
then how can I build that mapStateToProp function and use it with connect?
Say the todo list data is like :
{
title: "TodoList",
version:"1.0",
todolist: [
{id:"id1", todocontent: "todo 1"},
{id:"id2", todocontent: "todo 2"}
]
}
Something like this:
class YourComponent extends Component {
render () {
return (
<div>
{this.props.items.forEach((item) => {
return <todo id={item.id} todocontent={item.todocontent} />
})}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
items: state.<yourReducerName>.todolist
}
}
connect(mapStateToProps)(YourComponent)
Related
I have a React Component ValidationEditor that calls another custom React Component Property, which holds the property value a array of strings.
interface props extends ValidationDataProperty {
tree: Tree;
}
const ValidationEditor = ({ tree, id, ifData, thenData, elseData }: props) => {
const { classes } = useStyles();
const { state, dispatch } = useContext(PropertyContext);
const updateValidation = modifyConditionalProperty(id, dispatch);
return (
<Group className={classes.validator}>
<Box></Box>
<Text>If</Text>
<Property
value={[""]}
mode="edit"
tree={tree}
onClick={(e: { target: { value: SetStateAction<string[]> } }) =>
updateValidation({ ifData: { ...ifData, value: e.target.value } })
}
/>
<NativeSelect
data={_.keys(ComparisonType)}
required
value={ifData.comparison}
onChange={(e: { target: { value: SetStateAction<string> } }) =>
updateValidation({
ifData: { ...ifData, comparison: e.target.value },
})
}
/>{" "}
<TextInput
placeholder="Provide conditional value"
required
value={ifData.value}
sx={{ flex: 1 }}
onChange={(e: { target: { value: SetStateAction<string> } }) =>
updateValidation({ ifData: { ...ifData, value: e.target.value } })
}
/>
</Group>
);
};
export default ValidationEditor;
Now I want to add a onClick event to the Property React Component . Basically onClick I want to call an action -> modifyConditionalProperty(id, dispatch) , that will update the store via a reducer. I am only struggling to make it work only for my custom React component Property rest its working fine.
This is how the Property component looks like
interface PropertyProps {
value?: string[];
mode: "edit" | "display";
tree: Tree;
onClick?: (e: { target: { value: SetStateAction<string[]> } }) => void;
}
const Property = ({ value, mode, tree }: PropertyProps) => {
const [currentValue, setCurrentValue] = useState<string[]>(value || []);
const [displayMode, toggle] = useToggle(mode, ["edit", "display"]);
console.log(value) // ["a", "b", "c"]
return (
<Box>
.....
</Box>
);
};
export default Property;
I'm trying to integrate THEOPlayer in my project and I want to customize styles depending on certain events. For instance, I would love to hide the toolbar and show an overlay image when the video is paused.
They do expose some CSS classes that I can change manually but my question is, how do I change the values in CSS on a specific event. Since the player is imported as a single JSX element I don't know how to add custom classes to its specific parts. So I would like to know if there is another way.
Here is a component where an instance of Player is created:
class Player extends React.Component {
_player = null;
_el = React.createRef();
componentDidMount() {
const { source, onPlay, onPause } = this.props;
if (this._el.current) {
this._player = new window.THEOplayer.Player(this._el.current, {
libraryLocation:
"https://cdn.myth.theoplayer.com/7aff3fa6-f92e-45f9-a40e-1bce9911b073/",
});
this._player.source = source;
this._player.addEventListener("play", onPlay);
this._player.addEventListener("pause", onPause);
}
}
componentWillUnmount() {
if (this._player) {
this._player.destroy();
}
}
render() {
return (
<div
className={
"theoplayer-container video-js theoplayer-skin vjs-16-9 THEOplayer"
}
ref={this._el}
>
</div>
);
}
}
export default Player;
And that's a part of code where I want to change styles onPlay and onPause
<div className={"player-container"}>
<Player
source={source}
onPlay={() => {
console.log("playing");
}}
onPause={() => {
console.log("paused");
}}
/>
</div>
Use like this
state = {
play: false,
pause: true,
}
const playFn = () => {
this.setState = ({
play: true,
pause: false,
})
}
const pauseFn = () => {
this.setState = ({
play: false,
pause: true,
})
}
<div className={"player-container"}>
<Player
source={source}
onPlay={playFn}
onPause={pauseFn}
activatePlayClasses={play}
activatePauseClasses={pause}
bg={'https://example/example.jpg'}
/>
</div>
// on Player component
const { source, onPlay, onPause, activatePauseClasses, activatePlayClasses , bg} = this.props;
render() {
return (
<div
className={
`theoplayer-container video-js theoplayer-skin vjs-16-9 THEOplayer
${activatePauseClasses ? 'your pause class' : ''}
${activatePlayClasses ? 'your play class' : ''}`
}
style={{backgroundImage: `url(${bg})`}}
ref={this._el}
>
</div>
);
}
I have updated code
Trying to toggle open a modal from another component with redux. Almost there but not really sure how to finish it up - been looking around for a clear answer!
On the HomeScreen component (the main component), to activate the openModal method on the AddCircleModal component, causing the Modal to open.
The Modal - AddCircleModal: Using redux, I can successfully close the modal if I open it manually in the code
class AddCircleModal extends React.Component {
state = {
top: new Animated.Value(screenHeight),
modalVisible: false
}
// componentDidMount() {
// this.openModal()
// }
openModal = () => {
Animated.spring(this.state.top, {
toValue: 174
}).start()
this.setState({modalVisible: true})
}
closeModal = () => {
Animated.spring(this.state.top, {
toValue: screenHeight
}).start()
this.setState({modalVisible: false})
}
render() {
return (
<Modal
transparent={true}
visible={this.state.modalVisible}
>
<AnimatedContainer style={{ top: this.state.top, }}>
<Header />
<TouchableOpacity
onPress={this.closeModal}
style={{ position: "absolute", top: 120, left: "50%", marginLeft: -22, zIndex: 1 }}
>
<CloseView style={{ elevation: 10 }}>
<FeatherIcon name="plus" size={24} />
</CloseView>
</TouchableOpacity>
<Body />
</AnimatedContainer>
</Modal>
)
}
}
function mapStateToProps(state) {
return { action: state.action }
}
function mapDispatchToProps(dispatch) {
return {
closeModal: () =>
dispatch({
type: "CLOSE_MODAL"
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddCircleModal)
HomeScreen: The other component that I want to toggle from
//redux
import { connect } from 'react-redux'
import styles from './Styles'
class HomeScreen extends React.Component {
constructor() {
super();
this.state = {
};
}
toggleOpenCircleModal = () => {
// this.openModal() - what do I do with this to call the openModal function in the modal component?
console.log('owwwww weeeee')
}
render() {
return (
<SafeAreaView>
<HomeHeader openModal={this.toggleOpenCircleModal}/> - this method is because I'm calling toggleOpenCircleModal from a button in the header of the home screen. It works as it outputs the 'owwwww weeeee' string to the console.
<SafeAreaView style={{ width: '100%', flex: 1}} />
<AddCircleModal />
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return { action: state.action }
}
function mapDispatchToProps(dispatch) {
return {
openModal: () =>
dispatch({
type: "OPEN_MODAL"
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
modalToggle: The reducer
const initialState = {
action: ""
}
const modalToggle = (state = initialState, action) => {
switch (action.type) {
case "OPEN_MODAL":
return { ...state, action: "openModal" }
case "CLOSE_MODAL":
return { ...state, action: "closeModal" }
default:
return state
}
}
export default modalToggle
Right now, your components are not using redux store properly.
When you use mapStateToProps, you can access every redux store reducer. You can access every prop in them and these will be sent via props in your connected component. For instance:
//redux
import { connect } from 'react-redux'
import styles from './Styles'
class HomeScreen extends React.Component {
constructor() {
super();
this.state = {
};
}
toggleOpenCircleModal = () => {
if(this.props.action === 'openModal') {
this.props.openModal();
} else {
this.props.closeModal();
}
}
render() {
const { action } = this.props; // this.props.action is coming from Redux Store
return (
<SafeAreaView>
{action} // this will be 'openModal'
</SafeAreaView>
);
}
}
function mapStateToProps(state) {
return { action: state.action } // this will be available in HomeScreen as props.action
}
function mapDispatchToProps(dispatch) {
return {
openModal: () =>
dispatch({
type: "OPEN_MODAL"
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
You can read more on https://react-redux.js.org/using-react-redux/connect-mapstate.
The same goes for mapDispatchToProps. In your case, openModal will be available in props.openModal in your HomeScreen component. You can read more on https://react-redux.js.org/using-react-redux/connect-mapdispatch
Based on this, in your AddCircleModal component, you should be using props.action to evaluate if the modal should be visible. (props.action === 'openModal').
If you want to open or close your modal, you'll just need to call the openModal or closeModal dispatch call in your component. In HomeScreen component, in your function toggleOpenCircleModal, you will call openModal() or closeModal() depending on props.action === 'openModal'.
Lastly, you should be using just a boolean value to check for the modal visibility, instead of a string, if that's the only purpose for your reducer.
const initialState = false;
const modalToggle = (state = initialState, action) => {
switch (action.type) {
case "OPEN_MODAL":
return true;
case "CLOSE_MODAL":
return false;
default:
return state
}
}
export default modalToggle
Very basic simple GET example for react-redux
I have a "MockAPI" which simulates a GET request to an API like so:
const dashboards = [
{
"Id":1,
"title":"Overview"
},
{
"Id":2,
"title":"Overview"
},
{
"Id":3,
"title":"Overview"
},
{
"Id":4,
"title":"Overview"
}
];
class DashboardApi {
static getAllDashboards() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Object.assign([], dashboards));
}, delay);
});
}
}
I am trying to develop in a react-redux flow of dispatching an action via a button click and then updating the component via the redux store.
Here is my component code:
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import * as dashboardActions from '../../actions/dashboardActions';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.loadDashboards = this.loadDashboards.bind(this);
}
loadDashboards() {
this.props.dispatch(dashboardActions.loadDashboards());
}
dashboardItem(dashboard, index) {
return <p key={index}>{dashboard.title}</p>;
}
render() {
return (
<div>
<h1>
Hello World!
<button onClick={this.loadDashboards}>load</button>
</h1>
{this.props.dashboards.map(this.dashboardItem)}
</div>
);
}
}
HomePage.propTypes = {
dashboards: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
return {
dashboards: state.dashboards
};
}
export default connect(mapStateToProps)(HomePage);
And here is my dashboardActions.js:
import * as types from './actionTypes';
import dashboardApi from '../mockApi/mockDashboardApi';
export function loadDashboardsSuccess(dashboards) {
return { type: types.LOAD_DASHBOARDS_SUCCESS, dashboards };
}
export function loadDashboards() {
return dispatch => {
return dashboardApi
.getAllDashboards()
.then(dashboards => {
dispatch(loadDashboardsSuccess(dashboards));
});
};
}
And here is my reducer:
import initialState from './initialState';
import * as types from '../actions/actionTypes';
export default function dashboardReducer(state = initialState.dashboards, action) {
switch(action.types) {
case types.LOAD_DASHBOARDS_SUCCESS:
return action.dashboards;
default:
return state;
}
}
I am trying to get the onClick to load in the dashboards array and to render as <p> tags simply displaying the title value. Unfortunately it is not happening.
I see that the LOAD_DASHBOARDS_SUCCESS action is getting loaded, but I see that the dashboards property in the store is still an empty array instead of showing the returned data...
What am I missing here?
You've got a typo in your reducer. switch(action.types) should be switch(action.type) with no 's'
We are trying to inject dynamic children into a react app that is using React-Redux and Redux, and are experiencing an issue with the binding on the children props. I've distilled the problem into the following example code (JSFiddle). The issue is that the original rendered element updates just fine, but the dynamically injected portion does not. The strange thing is that the update is picked up in the redux store, and is fed to the props correctly.
const initialState = {
renderProperty: "Red Fish",
getChildrenProperty: "Blue Fish",
}
function MainReducer(state = initialState, action) {
switch (action.type) {
case 'PROBLEM_CHILD_INSIDE_RENDER':
return Object.assign({}, state, {
renderProperty: action.mutatedProperty
})
case 'PROBLEM_CHILD_INSIDE_GET_CHILDREN':
return Object.assign({}, state, {
getChildrenProperty: action.mutatedProperty
})
default:
return state
}
}
const store = Redux.createStore(MainReducer);
function mapStateToProps(state) {
return {
renderProperty : state.renderProperty,
getChildrenProperty : state.getChildrenProperty
}
}
function mapDispatchToProps(dispatch) {
return {
actions: Redux.bindActionCreators(actionCreators, dispatch)
};
}
class ProblemChild extends React.Component {
constructor() {
super();
this.childrenInjected = false;
this.state = {children: null};
}
/**
* Add children in setDynamicChildren() versus within render()
*/
componentDidMount() {
this.setDynamicChildren();
}
setDynamicChildren() {
this.setState({
children: this.getChildren()
});
}
getChildren() {
var me = this;
console.log(this);
return (
<div>
<br/>
<button style={{marginBottom: '10px'}}
onClick={me._handleGetChildrenAction.bind(me)} >UI State Change Action for prop in getChildren()</button>
<br/>
<span>prop in getChildren(): <b>{me.props.getChildrenProperty}</b></span>
</div>
)
}
render() {
var me = this,
childrenInjected = me.childrenInjected;
console.log(this.props);
if(me.state.children && !me.childrenInjected) {
return (
<div >
<button style={{marginBottom: '10px'}}
onClick={me._handleRenderAction.bind(me)} > UI State Change Action for prop in render()</button>
<br/>
<span>prop in render(): <b>{me.props.renderProperty}</b></span>
<br/>
{this.state.children} <br/>
</div>
)
}
else {
return (
<div>placeholder, yo</div>
)
}
}
_handleRenderAction() {
var me = this;
store.dispatch(actionForPropInsideRender('One Fish!'));
}
_handleGetChildrenAction() {
var me = this;
store.dispatch(actionForPropInsideGetChildren('Two Fish!'));
}
}
ProblemChild = ReactRedux.connect(mapStateToProps,mapDispatchToProps)(ProblemChild);
function actionForPropInsideRender(mutatedProperty) {
return {
type: 'PROBLEM_CHILD_INSIDE_RENDER',
mutatedProperty
}
}
function actionForPropInsideGetChildren(mutatedProperty) {
return {
type: 'PROBLEM_CHILD_INSIDE_GET_CHILDREN',
mutatedProperty
}
}
const actionCreators = {actionCreatorForPropInsideRender, actionCreatorForPropInsideGetChildren};
function actionCreatorForPropInsideRender(state, mutatedProperty) {
let newState = state.setIn(['uiState', 'renderProperty'], mutatedProperty),
nodeValue;
nodeValue = newState.getIn(['uiState', 'renderProperty']);
return newState;
}
function actionCreatorForPropInsideGetChildren(state, mutatedProperty) {
let newState = state.setIn(['uiState', 'getChildrenProperty'], mutatedProperty),
nodeValue;
nodeValue = newState.getIn(['uiState', 'getChildrenProperty']);
return newState;
}
ReactDOM.render(
<div>
<ReactRedux.Provider store={store}>
<ProblemChild />
</ReactRedux.Provider>
</div>,
document.getElementById('container')
);
setDynamicChildren() {
this.setState({
children: this.getChildren()
});
}
Is there a reason your intermixing redux state and local class state changes? From my experience this is asking for weird behaviour. I would swap this.setState for this.props.dispatch(action...) to update the redux store state. Do you still have issues when the complete state is now in redux? Otherwise a snapshot of redux dev tools state changes would be helpful in these cases.