I am using redux-orm to create models for normalization and denormalization. I find that when I create a schema, I get the error:
Uncaught Error: Schema has been renamed to ORM. Please import ORM instead of Schema from Redux-ORM
I have used both the 0.90-rc1
which is the version installed by default as well as `0.8.4
when running this code:
import { Schema } from 'redux-orm'
import Todo from './Todo'
import Tag from './Tag'
import User from './User'
const schema = new Schema()
schema.register(Todo, Tag, User)
export default schema
However I find that the documentation and code for schemas within redux-orm is still in existance.
If I switch from
import { Schema } from 'redux-orm'
to
import { ORM as Schema } from 'redux-orm'
the code works but I get an error specifying that reducer
method is not defined here:
import { schema } from './models'
console.log(schema)
const rootReducer = combineReducers({
orm: schema.reducer(),
selectedUserId: selectedUserIdReducer
})
Most of the code is based of the primer here
My Models look like this:
ValidatingModel.js
import { PropTypes } from 'react'
import { Model } from 'redux-orm'
import propTypesMixin from 'redux-orm-proptypes'
const ValidatingModel = propTypesMixin(Model)
export default ValidatingModel
Todo.js
import { fk, many } from 'redux-orm'
import { PropTypes } from 'react'
import ValidatingModel from './ValidatingModel'
import User from './User'
import Tag from './Tag'
import { CREATE_TODO, MARK_DONE, DELETE_TODO, ADD_TAG_TO_TODO, REMOVE_TAG_FROM_TODO } from '../actionTypes'
export default class Todo extends ValidatingModel {
static reducer (state, action, Todo, session) {
const { payload, type } = action
switch (type) {
case CREATE_TODO:
const tagIds = payload.tags.split(',').map(str => str.trim())
const props = Object.assign({}, payload, { tags: tagIds })
Todo.create(props)
break
case MARK_DONE:
Todo.withId(payload).set({ done: true })
break
case DELETE_TODO:
Todo.withId(payload).delete()
break
case ADD_TAG_TO_TODO:
Todo.withId(payload.todo).tags.add(payload.tag)
break
case REMOVE_TAG_FROM_TODO:
Todo.withId(payload.todo).tags.remove(payload.tag)
break
}
}
}
Todo.modelName = 'Todo'
Todo.propTypes = {
id: PropTypes.number,
text: PropTypes.string.isRequired,
done: PropTypes.bool.isRequired,
user: PropTypes.oneOf([PropTypes.instanceOf(User), PropTypes.number]),
tags: PropTypes.arrayOf(PropTypes.oneOf([
PropTypes.number,
PropTypes.instanceOf(Tag)
]))
}
Todo.defaultProps = {
done: false
}
Todo.fields = {
user: fk('User', 'todos'),
tags: many('Tag', 'todos')
}
Tag.js
import ValidatingModel from './ValidatingModel'
import { PropTypes } from 'react'
import { CREATE_TODO, ADD_TAG_TO_TODO } from '../actionTypes'
export default class Tag extends ValidatingModel {
static reducer (state, action, Tag, session) {
const { payload, type } = action
switch (type) {
case CREATE_TODO:
const tags = payload.tags.split(',')
const trimmed = tags.map(name => name.trim())
trimmed.forEach(name => Tag.create(name))
break
case ADD_TAG_TO_TODO:
if (!Tag.filter({ name: payload.tag }).exists()) {
Tag.create({ name: payload.tag })
}
break
}
}
}
Tag.modelName = 'Tag'
Tag.backend = {
idAttribute: 'name'
}
Tag.propTypes = {
name: PropTypes.string
}
This is the User model, I have added a no-op reducer after seeing @squiroid original answer
import ValidatingModel from './ValidatingModel'
import { PropTypes } from 'react'
export default class User extends ValidatingModel {
static reducer (state, action, User, session) {
return state
}
}
User.modelName = 'User'
User.propTypes = {
id: PropTypes.number,
name: PropTypes.string
}
First, I needed to use ORM
instead of the Schema
. So I changed the import for Schema
to:
import { ORM as Schema } from 'redux-orm'
Secondly, I had to change the signature of the reducer to:
static reducer (action, SessionSpecificModel, session)
the redux-orm docs have been updated, the second argument is not the state of the reducer but a session specific model.
from the signature in the documentation:
static reducer (state, action, Model, session)
Thirdly, I had to change the code in the todo
to use toRefArray
or toModelArray
in order to call map
on a list of Todo
and Tag
instances:
return orm.Todo.filter({ user: userId }).toModelArray().map(todo => {
const obj = Object.assign({}, todo.ref)
obj.tags = todo.tags.toRefArray().map(tag => tag.name)
return obj
})
Fourth, I had to resolve the Model
class from session
instance.
I am still finding issues with creating a Todo
where:
session.Todo.create(props)
throws the error
:
Invalid prop tags[0]
supplied to Todo.create
.
when called with the props
:
{
"text":"Test my data",
"tags":[
"urgent",
"personal"
],
"user":0
}
The validations seem to be interfering with creating the model. When creating the tags, specify the PropTypes
to be:
const { string, number, arrayOf oneOfType, instanceOf } = PropTypes
Todo.propTypes = {
text: string,
user: oneOfType([
number,
instanceOf(User)
]),
tags: oneOfType([
arrayOf(string),
arrayOf(
instanceOf(Tag)
)
])
}
In redux-orm when bootstrapping or creating models, you can either provide the id or the instance of the model to the related property. Thus, it would be nessecary to ensure that both are accepted by the Model
.