I am new at using RematchJS and have managed to display hardcoded Objects from an array. The dispatcher (addTopicAsync) however does not seems to update the Array, when I try to add a new Object.
The Array is briefly updated and the topic is flashes on the screen, but the Array is empty shortly afterwards.
My model code:
import { createModel } from "#rematch/core";
import { RootModel } from ".";
export interface Topic {
topic: String
}
export interface TopicsList {
list: Array<Topic>
}
const TOPICS_LIST_STATE = {
list: []
}
export const topics = createModel<RootModel>()({
state: TOPICS_LIST_STATE as TopicsList,
reducers: {
addTopic: (state, topic) => {
return { list: [...state.list, { topic }] }
},
clearTopics: () => {
return { list: [] }
}
},
effects: (dispatch) => ({
async addTopicAsync(topic: string) {
await dispatch.topics.addTopic(topic)
},
async clearTopicsAsync() {
await dispatch.topics.clearTopics()
}
})
});
My application code:
// eslint-disable-next-line #typescript-eslint/no-unused-vars
import { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState, Dispatch } from '#vanilla/data'
import { Topic } from 'libs/data/src/lib/models/topics';
export function App() {
const [topic, setTopic] = useState("aaa")
const topicsState = useSelector((state: RootState) => state.topics)
const dispatch = useDispatch<Dispatch>()
const topicChange = (event: React.ChangeEvent<HTMLInputElement>) => {
event.preventDefault()
setTopic(event.target.value)
}
const updateTopicList = async () => {
await dispatch.topics.addTopicAsync(topic)
console.log('topicsState : ', topicsState.list)
}
return (
<>
<h3>Topics</h3>
<form>
<input type='text' value={topic} onChange={topicChange} />
<button onClick={() => { updateTopicList() }}> Add Topic</button>
</form>
<div className="container">
{topicsState.list.map((topicRecord: Topic, index: number) => (
<h5 key={index}>
{topicRecord.topic}
</h5>
))}
</div>
</>
)
}
export default (App)
Related
Table with pinia store equipmentlist, fethced from usequipments but table does not rerender
VueTableComponent
<script setup lang="ts">
import { useEquipmentStore } from '~/store/equipmentStore'
import useEquipments from '~/composables/equipments'
const { getEquipmentList } = useEquipments()
onBeforeMount(() => { getEquipmentList() })
const state = useEquipmentStore()
const equipmentList: any = state.equipmentList
const loaded = state.loaded
</script>
<template>
<el-table :key="loaded" :data="equipmentList" style="width: 100%">
<el-table-column type="expand">
<template #default="props">
ID: {{ props.row.id }}
</template>
</el-table-column>
<el-table-column label="ID" prop="id" />
<el-table-column label="Name" prop="name" />
</el-table>
</template>
Typescript File for all CRUD Operations equipment.ts
import { useRouter } from 'vue-router'
import http from '../http-common'
import { useEquipmentStore } from '~/store/equipmentStore'
export default function useEquipments() {
const state = useEquipmentStore()
const errors = ref([]) // array of strings
const router = useRouter()
const getEquipmentList = async() => {
try {
console.log(state.equipmentList.length)
const response = await http.get('/equipment/list')
state.equipmentList = response.data
console.log(state.equipmentList.length)
console.log(state.equipmentList[0])
}
catch (error: any) {
console.log(error.message)
}
}
Equipment(Pinia)Store
import { defineStore } from 'pinia'
import type { Ref } from 'vue'
export const useEquipmentStore = defineStore('equipment', {
state: () => ({
equipment: ref({}) as any,
equipmentList: ref([]) as Ref<any[]>,
}),
actions: {
reset() {
this.equipment = {}
this.equipmentList = []
},
},
})
1. i called several times getEquipment list and it is faster done then i stored an initial equipment, 2. i clicked on the link on the left and fetched several times more and as u can see there is something fetchd but not displayed, 3. after repeating to home and again to Link the component is there and alll other next fetches do indeed function well
Main.ts
app.component('InputText', InputText)
app.mount('#app')
useEquipments().initDB()
}
same fetching class equipment.ts
const initDB = () => {
try {
if (state.equipmentList.length === 0) { storeEquipment({ id: 1, name: 'Firma' }) }
else {
for (const equipment of state.equipmentList) {
console.log(equipment)
if (equipment === 'Firma')
state.equipmentList.splice(state.equipmentList.indexOf(equipment), 1)
}
}
}
catch (error: any) {
console.log(error.message)
}
}
I am creating a simple game react app and when I try to add a player to my players list it seems to be creating an infinite loop and I'm not sure why. I tried to use useEffect to render the player list on initial load but that didn't help so I removed it for now to simplify. Any ideas what I could be doing differently?
App.js
import React, { useEffect } from 'react'
import {useDispatch, useSelector} from 'react-redux';
import './App.css';
import {setPlayerName, increaseCurrentPlayerId, decreaseCurrentPlayerId, addPlayerToList} from './redux/reducers/playerReducer';
function App() {
const dispatch = useDispatch()
const playerName = useSelector(state => state.playerName);
const playerList = useSelector(state => state.playerList);
const currentPlayerId = useSelector(state => state.currentPlayerId)
// dispatch(addPlayerToList('Test'))
const addPlayer = (player) => {
dispatch(addPlayer(player))
dispatch(setPlayerName(''))
}
const renderPlayerList = () => {
if (playerList.length < 1) {
return (
<div>
No Players
</div>
)
} else {
return (
playerList.map(p =>
<p>p.name</p>
)
)
}
}
return (
<div className="App">
<input
type='text'
name='playerName'
onChange={({ target }) => dispatch(setPlayerName(target.value))}
required
/>
Name<br/>
<button type='button'
onClick={() => addPlayer(playerName)}
>
Add Player</button> <br />
<br />
</div>
);
}
export default App;
playerReducer.js
export const playerNameReducer = (state = '', action) => {
switch (action.type) {
case 'SET_PLAYER_NAME':
return action.data;
default:
return state;
}
};
export const playerListReducer = (state = null, action) => {
switch (action.type) {
case 'ADD_PLAYER':
return [...state, action.data];
default:
return state;
}
};
Action Creators
export const setPlayerName = playerName => {
return {
type: 'SET_PLAYER_NAME',
data: playerName,
};
};
export const addPlayerToList = player => {
return {
type: 'ADD_PLAYER',
data: player,
};
};
addPlayer calls itself
const addPlayer = (player) => {
dispatch(addPlayer(player))
}
Thanks for everyone stop here whether you inspire me or not.In App.js,there is error message:Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
This is very classic todolist project in reduxjs official document,but I reshape it with react and react-redux hooks.
Tree view down:
Index.js:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { createStore } from "redux";
import rootReducer from "./reducers";
import { Provider } from "react-redux";
const store = createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
App.js:
import React from "react";
import Addtodo from "./components/Addtodo";
import Footer from "./components/Footer";
import Todolist from "./components/Todolist";
const App = () => {
return (
<>
<Addtodo />
<Todolist />
<Footer />
</>
);
};
export default App;
actions.js
let nextTodoId = 0;
const addtodo = (text) => {
return {
type: "ADD_TODO",
id: nextTodoId++,
text: text
};
};
const toggletodo = (id) => {
return { type: "TOGGLE_TODO", id: id };
};
const setfilter = (filter) => {
return {
type: "SET_VISIBILITY_FILTER",
filter: filter
};
};
const todoActions = { addtodo, toggletodo };
const filterActions = { setfilter };
const allActions = {
todoActions,
filterActions
};
export default allActions;
reducers.js
import { combineReducers } from "redux";
const todo = (state = {}, action) => {
switch (action.type) {
case "ADD_TODO":
return { id: action.id, text: action.text, completed: false };
case "TOGGLE_TODO":
if (state.id === action.id)
return { ...state, completed: !state.completed };
else return state;
default:
return state;
}
};
const todos = (state = [], action) => {
switch (action.type) {
case "ADD_TODO":
return [...state, todo(undefined, action)];
case "TOGGLE_TODO":
return state.map((s) => todo(s, action));
default:
return state;
}
};
const visibilityFilter = (state = "ALL", action) => {
switch (action.type) {
case "SET_VISIBILITY_FILTER":
if (state !== action.filter) return action.filter;
default:
return state;
}
};
const rootReducer = combineReducers({ todos, visibilityFilter });
export default rootReducer;
Addtodo.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import allActions from "../actions";
const Addtodo=() => {
const dispatch = useDispatch();
return(
<>
<input
ref={(node) => {
this.input = node;
}}
/>
<button
onClick={() => {
dispatch(allActions.todoActions.addtodo(this.input.value));
this.input.value = "";
}}
>
Add To Do
</button>
</>
);
};
export default Addtodo;
Todolist.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import allActions from "../actions";
const getvisibletodos = (todos = [], currentFilter) => {
switch (currentFilter) {
case "ALL":
return todos;
case "ACTIVE":
return todos.filter((todo) => todo.completed === false);
case "COMPLETED":
return todos.filter((todo) => todo.completed === true);
}
};
const Todolist = () => {
const todos = useSelector((state) =>
getvisibletodos(state.todos, state.visibilityFilter)
);
const dispatch = useDispatch();
return (
<ul>
{todos.map((todo) => (
<li
key={todo.id}
style={{ textDecoration: todo.completed ? "line-through" : "none" }}
onClick={() => dispatch(allActions.todoActions.toggletodo(todo.id))}
>
{todo.text}
</li>
))}
</ul>
);
};
export default Todolist;
Footer.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import allActions from "../actions";
const Filterlink = ({
children,
filter
}) => {
const active = useSelector(state => state.visibilityFilter ===filter);
const dispatch=useDispatch();
if (active) {
return <span>{children}</span>;
}
return (
<a href='#'
onClick={e => {
e.preventDefault();
dispatch(allActions.filterActions.setfilter(filter));
}}
>
{children}
</a>
);
};
export const Footer = () => (
<p>
Show:
{' '}
<FilterLink filter='ALL'>
All
</FilterLink>
{', '}
<FilterLink filter='ACTIVE'>
Active
</FilterLink>
{', '}
<FilterLink filter='COMPLETED'>
Completed
</FilterLink>
</p>
);
The codesandebox link available:https://codesandbox.io/s/lesson-27-28-29-transition-to-react-redux-hooks-stackoverflow-0fsh6
Wish everyone get new taste in coding everyday.
I'm trying to implement a sports betting filter, but am getting stuck when trying to dispatch a thunk to re-render the page based on a sport-specific key that I pass into the thunk. I have a dropdown list that has click listeners that dispatch a handler in the parent component (GamesList). I'm noticing my action creator (gotKey) keeps returning the default key('americanfootball_ncaaf') even when clicking on the NFL list item in the DropdownList component. Any help or direction is much appreciated. Thanks!
Redux Store
import axios from 'axios'
require('../../secrets')
const GOT_GAMES = 'GOT_GAMES'
const GOT_MONEYLINES = 'GOT_MONEYLINES'
const GOT_KEY = 'GOT_KEY'
const gotGames = games => ({type: GOT_GAMES, games})
const gotMoneyLines = games => ({type: GOT_MONEYLINES, games})
const gotKey = key => ({type: GOT_KEY, key})
const defaultSportKey = 'americanfootball_ncaaf'
const apiKey = process.env.API_KEY
export const getGames = (key = defaultSportKey) => async dispatch => {
try {
const {data} = await axios.get(
`https://api.the-odds-api.com/v3/odds/?apiKey=${apiKey}&sport=${key}®ion=us&mkt=spreads`
)
dispatch(gotGames(data))
dispatch(gotKey(key))
} catch (error) {
console.error(error)
}
}
export const getMoneyLines = (key = defaultSportKey) => async dispatch => {
try {
const {data} = await axios.get(
`https://api.the-odds-api.com/v3/odds/?apiKey=${apiKey}&sport=${key}®ion=us&mkt=h2h`
)
dispatch(gotMoneyLines(data))
dispatch(gotKey(key))
} catch (error) {
console.error(error)
}
}
const gamesState = {
spreadGames: [],
moneylineGames: [],
sportKey: ''
}
const gamesReducer = (state = gamesState, action) => {
// console.log('action' + action)
switch (action.type) {
case GOT_GAMES:
return {...state, spreadGames: action.games}
case GOT_MONEYLINES:
return {...state, moneylineGames: action.games}
case GOT_KEY:
return {...state, sportKey: action.key}
default:
return state
}
}
export default gamesReducer
Main GamesList component
import React from 'react'
import {connect} from 'react-redux'
import {getGames, getMoneyLines} from '../store/games'
import SingleGame from './SingleGame'
import DropdownList from './DropdownList.js'
class GamesList extends React.Component {
constructor() {
super()
this.handler = this.handler.bind(this)
}
componentDidMount() {
this.props.getGames()
this.props.getMoneyLines()
}
handler(key) {
this.props.getGames(key)
this.props.getMoneyLines(key)
}
render() {
// console.log(this.state)
const spreadList = this.props.spreadGames.data
const moneylineList = this.props.moneylineGames.data
if (!spreadList || !moneylineList) {
return <div>loading</div>
} else {
return (
<div>
<DropdownList handler={this.handler} />
{spreadList.map((game, index) => (
(Date.now().toString().slice(0, -3) < game.commence_time) ?
<div key={index}>
<SingleGame
spreads={game.sites[0]}
homeTeam={game.home_team}
teams={game.teams}
timeStart={game.commence_time}
moneylineGame={moneylineList[index]}
/>
</div> : null
))}
</div>
)
}
}
}
const mapStateToProps = state => ({
spreadGames: state.games.spreadGames,
moneylineGames: state.games.moneylineGames,
sportKey: state.games.sportKey
})
const mapDispatchToProps = dispatch => ({
getGames: () => dispatch(getGames()),
getMoneyLines: () => dispatch(getMoneyLines())
})
export default connect(mapStateToProps, mapDispatchToProps)(GamesList)
DropdownList Component
import React from 'react'
export default class DropdownList extends React.Component {
constructor(props) {
super()
this.state = {
listOpen: false
}
this.showList = this.showList.bind(this)
this.hideList = this.showList.bind(this)
}
showList(event) {
event.preventDefault()
this.setState({listOpen: true}, () => {
document.addEventListener('click', this.hideList)
})
}
hideList() {
this.setState({listOpen: false}, () => {
document.addEventListener('click', this.hideList)
})
}
render() {
return (
<div>
<div type="button" onClick={this.showList}>
Select A Sport
</div>
{this.state.listOpen ? (
<ul>
<li
onClick={() => {
this.props.handler('americanfootball_nfl')
}}
>
NFL
</li>
<li
onClick={() => {
this.props.handler('americanfootball_ncaaf')
}}
>
NCAA
</li>
</ul>
) : (
<li>test</li>
)}
</div>
)
}
}
You're not passing your key in mapDispatchToProps
const mapDispatchToProps = dispatch => ({
getGames: key => dispatch(getGames(key)), // <-- forward key
getMoneyLines: key => dispatch(getMoneyLines(key)) // <-- forward key
})
react-redux has a nicer api for this, you just pass your action creators directly
export default connect(mapStateToProps, { getGames, getMoneyLines })(GamesList)
https://react-redux.js.org/api/connect#example-usage
I have a react-in-rails application that utilizes the google-maps-react api. The app works fine locally but when deployed to heroku, any component that imports google-maps-react does not render. Since this is generally the landing page for most users, the app is not accessible at all.
When all the components that import or render google-maps-react are removed, the app deploys correctly.
import React from "react"
import MapContainer from "./MapContainer"
import StoreList from './StoreList'
class FindBar extends React.Component {
render () {
const {stores, openTab, success} = this.props
return (
<div className="findbar" >
<div className="mapcomponent">
<MapContainer
stores={stores}
openTab={openTab}
success={success}
/>
</div>
<br/>
<StoreList
stores={stores}
openTab={openTab}
/>
{this.props.success &&
<Redirect to="/user_home/opentabs" />
}
</div>
);
}
}
export default FindBar
import React, { Component } from 'react';
import { Button, Card } from 'reactstrap';
import { Map, GoogleApiWrapper, Marker, InfoWindow } from 'google-maps-react';
import UserHome from './UserHome.js'
import StoreMarkerWindow from './StoreMarkerWindow.js'
import InfoWindowEx from './InfoWindowEx.js'
const mapStyles = {
width: '100%',
height: '100vh',
};
class MapContainer extends Component {
constructor(props) {
super(props)
this.state = {
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
address: [],
location: {},
displayMarkers: [],
success: false,
}
}
componentDidMount = () => {
this.fetchMarkers()
}
componentDidUpdate = (prevProps) => {
if (prevProps.stores === this.props.stores){
return true
}
this.fetchMarkers()
}
openTab = () => {
console.log(this.state.selectedPlace.storeId)
// this.props.openTab(this.state.selectedPlace.storeId)
}
onClick = (props, marker, e) => {
this.setState({
selectedPlace: props,
activeMarker: marker,
showingInfoWindow: true
})
}
onClose = props => {
if (this.state.showingInfoWindow) {
this.setState({
showingInfoWindow: false,
activeMarker: null
});
}
}
fetchMarkers = () => {
const newMarkers = []
this.props.stores.map((store, index) => {
const location = `${store.address1}, ${store.city}, ${store.state}, ${store.zip}`
this.geocodeAddress(location)
.then((geoco)=>{
newMarkers.push({lat: geoco.lat,
lng: geoco.lng,
storeId: store.id,
name: store.establishmentname,
location: location,
info: store.additionalinfo,
})
this.setState({ displayMarkers:newMarkers})
})
})
}
// create a function that maps stores.address, stores.city, stores.state, stores.zipcode
// and returns it to the geocodeAddress and then geocodeAddress returns it to
// the displayMarkers
geocodeAddress = (address) => {
const geocoder = new google.maps.Geocoder()
return new Promise((resolve, reject) => {
geocoder.geocode({'address': address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
resolve(results[0].geometry.location.toJSON())
} else {
reject()
}
})
})
}
render() {
const{
activeMarker,
showingInfoWindow,
selectedPlace,
onMapOver,
}=this.props
return (
<div className="mapContainer" style={mapStyles}>
<Map
google={this.props.google}
onMouseover={this.onMapOver}
zoom={14}
style={mapStyles}
initialCenter={{
lat: 32.7091,
lng: -117.1580
}}
>
{this.state.displayMarkers.map((coordinates, index) => {
const{storeId, lat, lng, name, location, info} = coordinates
return (
<Marker onClick={this.onClick}
key={index}
id={storeId}
name={name}
position = {{lat, lng}}
location={location}
info= {info}
>
</Marker>
)
})}
<InfoWindowEx
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
onClose={this.onClose}
>
<div>
<StoreMarkerWindow
name={this.state.selectedPlace.name}
location={this.state.selectedPlace.location}
info={this.state.selectedPlace.info}
id={this.state.selectedPlace.id}
openTab={this.props.openTab}
/>
</div>
</InfoWindowEx>
</Map>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: 'xxxx'
})(MapContainer);
TypeError: t is not a function
at Object.a (windowOrGlobal.js:18)
at Object.<anonymous> (windowOrGlobal.js:5)
at Object.<anonymous> (windowOrGlobal.js:5)
at n (bootstrap:19)
at Object.<anonymous> (ScriptCache.js:3)
at n (bootstrap:19)
at Object.<anonymous> (GoogleApiComponent.js:5)
at n (bootstrap:19)
at Object.<anonymous> (index.js:5)
at n (bootstrap:19)