immerjs / immer

Create the next immutable state by mutating the current one
https://immerjs.github.io/immer/
MIT License
27.67k stars 848 forks source link

can't use in useReducer when value is React.Element #545

Closed Liuyll closed 4 years ago

Liuyll commented 4 years ago

šŸ› Bug Report

in react reducer hooks,i get a fatal error in process of using immer... i cant find relevant error ...

question:

var definite

const nodeRefs = useRef({})
const [node_store,dispatch] = useReducer(reducer,{})

this is reducer

function reducer(state,action) {
        switch(action.type){
        case 'add': {
           // can't work
            return  produce(state,(draft) => {
                // isValidElement(action.children) == true
                draft[action.id] = action.children
            })
        }
        }
   }

in this way render :

{Object.keys(node_store).map((id) => (
                <div 
                    ref={x => {
                        nodeRefs.current[id] = x
                    }}
                    key={id}
                >
                    {node_store[id]}
                </div>
   ))}

occured error Uncaught TypeError: Cannot assign to read only property 'validated' of object '#<Object>'

but I toggle the reducer in next:

 case 'add': {
            return {
                 ...state,
                 [action.id]: action.children
            }   
        }

this can work ...

or i toggle this way to render:

 {Object.keys(node_store).map((id) => (
                isValidElement(node_store[id]) && node_store[id]
  ))
}

Link to repro

https://github.com/Liuyll/minimal-repro

Environment

mweststrate commented 4 years ago

If autofreeze is enabled, all plain objects in the tree will automatically be frozen, this includes react component instances. So this will break react itself that assumes it can modify the internal state of the element.

Some fixes for this issue would be:

Liuyll commented 4 years ago

react element is not extensible so i cant setting action.children[immerable] = false but i fix the issue by turn off autoFreeze . thanks help very much

tyscorp commented 4 years ago

Another option to solve this is to React.cloneElement at render time.